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 <functional> // for mem_fn
14 : : #include <limits>
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 : : #include <mozilla/Maybe.h>
32 : :
33 : : #include "gi/arg-cache.h"
34 : : #include "gi/arg-inl.h"
35 : : #include "gi/arg-types-inl.h"
36 : : #include "gi/arg.h"
37 : : #include "gi/boxed.h"
38 : : #include "gi/closure.h"
39 : : #include "gi/foreign.h"
40 : : #include "gi/function.h"
41 : : #include "gi/fundamental.h"
42 : : #include "gi/gerror.h"
43 : : #include "gi/gtype.h"
44 : : #include "gi/info.h"
45 : : #include "gi/js-value-inl.h"
46 : : #include "gi/object.h"
47 : : #include "gi/param.h"
48 : : #include "gi/union.h"
49 : : #include "gi/value.h"
50 : : #include "gi/wrapperutils.h" // for GjsTypecheckNoThrow
51 : : #include "gjs/auto.h"
52 : : #include "gjs/byteArray.h"
53 : : #include "gjs/enum-utils.h" // for operator&, operator|=, operator|
54 : : #include "gjs/jsapi-util.h"
55 : : #include "gjs/macros.h"
56 : : #include "util/log.h"
57 : :
58 : : using mozilla::Maybe, mozilla::Nothing, mozilla::Some;
59 : :
60 : : enum ExpectedType {
61 : : OBJECT,
62 : : FUNCTION,
63 : : STRING,
64 : : LAST,
65 : : };
66 : :
67 : : static const char* expected_type_names[] = {"object", "function", "string"};
68 : : static_assert(G_N_ELEMENTS(expected_type_names) == ExpectedType::LAST,
69 : : "Names must match the values in ExpectedType");
70 : :
71 : 360 : static constexpr void gjs_gi_argument_set_array_length(GITypeTag tag,
72 : : GIArgument* arg,
73 : : size_t value) {
74 [ - + - - : 360 : switch (tag) {
+ + + +
- ]
75 : 0 : case GI_TYPE_TAG_INT8:
76 : 0 : gjs_arg_set<int8_t>(arg, value);
77 : 0 : break;
78 : 1 : case GI_TYPE_TAG_UINT8:
79 : 1 : gjs_arg_set<uint8_t>(arg, value);
80 : 1 : break;
81 : 0 : case GI_TYPE_TAG_INT16:
82 : 0 : gjs_arg_set<int16_t>(arg, value);
83 : 0 : break;
84 : 0 : case GI_TYPE_TAG_UINT16:
85 : 0 : gjs_arg_set<uint16_t>(arg, value);
86 : 0 : break;
87 : 73 : case GI_TYPE_TAG_INT32:
88 : 73 : gjs_arg_set<int32_t>(arg, value);
89 : 73 : break;
90 : 5 : case GI_TYPE_TAG_UINT32:
91 : 5 : gjs_arg_set<uint32_t>(arg, value);
92 : 5 : break;
93 : 17 : case GI_TYPE_TAG_INT64:
94 : 17 : gjs_arg_set<int64_t>(arg, value);
95 : 17 : break;
96 : 264 : case GI_TYPE_TAG_UINT64:
97 : 264 : gjs_arg_set<uint64_t>(arg, value);
98 : 264 : break;
99 : 0 : default:
100 : : g_assert_not_reached();
101 : : }
102 : 360 : }
103 : :
104 : : GJS_JSAPI_RETURN_CONVENTION
105 : 2 : static bool report_typeof_mismatch(JSContext* cx, const char* arg_name,
106 : : JS::HandleValue value,
107 : : ExpectedType expected) {
108 : 2 : gjs_throw(cx, "Expected type %s for argument '%s' but got type %s",
109 : 2 : expected_type_names[expected], arg_name,
110 : : JS::InformalValueTypeName(value));
111 : 2 : return false;
112 : : }
113 : :
114 : : GJS_JSAPI_RETURN_CONVENTION
115 : 0 : static bool report_gtype_mismatch(JSContext* cx, const char* arg_name,
116 : : JS::Value value, GType expected) {
117 : 0 : gjs_throw(
118 : : cx, "Expected an object of type %s for argument '%s' but got type %s",
119 : : g_type_name(expected), arg_name, JS::InformalValueTypeName(value));
120 : 0 : return false;
121 : : }
122 : :
123 : : GJS_JSAPI_RETURN_CONVENTION
124 : 3 : static bool report_invalid_null(JSContext* cx, const char* arg_name) {
125 : 3 : gjs_throw(cx, "Argument %s may not be null", arg_name);
126 : 3 : return false;
127 : : }
128 : :
129 : : // Overload operator| so that Visual Studio won't complain
130 : : // when converting unsigned char to GjsArgumentFlags
131 : 30745 : GjsArgumentFlags operator|(
132 : : GjsArgumentFlags const& v1, GjsArgumentFlags const& v2) {
133 : 30745 : return static_cast<GjsArgumentFlags>(std::underlying_type<GjsArgumentFlags>::type(v1) |
134 : 30745 : std::underlying_type<GjsArgumentFlags>::type(v2));
135 : : }
136 : :
137 : : namespace Gjs {
138 : : namespace Arg {
139 : :
140 : : // Arguments Interfaces:
141 : : //
142 : : // Each of these types are meant to be used to extend each Gjs::Argument
143 : : // implementation, taking advantage of the C++ multiple inheritance.
144 : :
145 : : struct BasicType {
146 : 1800 : constexpr explicit BasicType(GITypeTag tag) : m_tag(tag) {
147 : 1800 : g_assert(GI_TYPE_TAG_IS_BASIC(tag));
148 : 1800 : }
149 : :
150 : : GITypeTag m_tag : 5;
151 : : };
152 : :
153 : : struct HasTypeInfo {
154 : 5573 : constexpr explicit HasTypeInfo(GI::TypeInfo info) {
155 : 5573 : GI::detail::Pointer::to_stack(GI::detail::Pointer::get_from(info),
156 : : &m_type_info);
157 : 5573 : }
158 : :
159 : 21580 : Maybe<ReturnTag> return_tag() const { return Some(ReturnTag{m_type_info}); }
160 : :
161 : : GI::StackTypeInfo m_type_info;
162 : : };
163 : :
164 : : struct Transferable {
165 : 14222 : constexpr Transferable() : m_transfer(GI_TRANSFER_NOTHING) {}
166 : : constexpr explicit Transferable(GITransfer transfer)
167 : : : m_transfer(transfer) {}
168 : : GITransfer m_transfer : 2;
169 : : };
170 : :
171 : : struct Nullable {
172 : 19499 : constexpr Nullable() : m_nullable(false) {}
173 : : bool m_nullable : 1;
174 : :
175 : : bool handle_nullable(JSContext* cx, GIArgument* arg, const char* arg_name);
176 : :
177 : 591 : constexpr GjsArgumentFlags flags() const {
178 [ + + ]: 591 : return m_nullable ? GjsArgumentFlags::MAY_BE_NULL
179 : 591 : : GjsArgumentFlags::NONE;
180 : : }
181 : : };
182 : :
183 : : struct Positioned {
184 : 1773 : void set_arg_pos(int pos) {
185 : 1773 : g_assert(pos <= Argument::MAX_ARGS &&
186 : : "No more than 253 arguments allowed");
187 : 1773 : m_arg_pos = pos;
188 : 1773 : }
189 : :
190 : 810 : constexpr bool set_out_parameter(GjsFunctionCallState* state,
191 : : GIArgument* arg) {
192 : 810 : gjs_arg_unset(&state->out_cvalue(m_arg_pos));
193 : : // The value passed to the function is actually the address of the out
194 : : // C value
195 : 810 : gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
196 : 810 : return true;
197 : : }
198 : :
199 : 76 : constexpr bool set_inout_parameter(GjsFunctionCallState* state,
200 : : GIArgument* arg) {
201 : 76 : state->out_cvalue(m_arg_pos) = state->inout_original_cvalue(m_arg_pos) =
202 : : *arg;
203 : 76 : gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
204 : 76 : return true;
205 : : }
206 : :
207 : : uint8_t m_arg_pos = 0;
208 : : };
209 : :
210 : : struct ExplicitArray {
211 : : uint8_t m_length_pos;
212 : : GIDirection m_length_direction : 2;
213 : :
214 : 1115 : ExplicitArray(int pos, GIDirection direction)
215 : 1115 : : m_length_pos(pos), m_length_direction(direction) {
216 : 1115 : g_assert(pos >= 0 && pos <= Argument::MAX_ARGS &&
217 : : "No more than 253 arguments allowed");
218 : 1115 : }
219 : : };
220 : :
221 : : struct BasicCArray {
222 : 644 : constexpr explicit BasicCArray(GITypeTag element_tag)
223 : 644 : : m_element_tag(element_tag) {
224 : 644 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag));
225 : 644 : }
226 : :
227 : : GITypeTag m_element_tag;
228 : : };
229 : :
230 : : struct ZeroTerminatedArray {
231 : 339 : constexpr explicit ZeroTerminatedArray(const GI::TypeInfo) {}
232 : :
233 : 26 : bool in(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
234 : : const char* arg_name, GjsArgumentFlags flags,
235 : : JS::HandleValue value) {
236 : 26 : return gjs_value_to_basic_array_gi_argument(
237 : : cx, value, element_tag, GI_ARRAY_TYPE_C, arg, arg_name,
238 : 26 : GJS_ARGUMENT_ARGUMENT, flags);
239 : : }
240 : :
241 : 43 : bool out(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
242 : : JS::MutableHandleValue value) {
243 : 43 : return gjs_array_from_basic_zero_terminated_array(
244 : 43 : cx, value, element_tag, gjs_arg_get<void*>(arg));
245 : : }
246 : :
247 : 45 : void release_container(GIArgument* arg) {
248 [ + - ]: 45 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
249 : 45 : }
250 : :
251 : 38 : void release_contents(GIArgument* arg) {
252 : 38 : char** array = gjs_arg_get<char**>(arg);
253 [ + + ]: 139 : for (size_t ix = 0; array[ix]; ix++)
254 : 101 : g_free(array[ix]);
255 : 38 : }
256 : :
257 : 27 : Maybe<ReturnTag> return_tag() const {
258 : 27 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
259 : : }
260 : : };
261 : :
262 : : struct GArrayContainer {
263 : 19 : constexpr explicit GArrayContainer(const GI::TypeInfo) {}
264 : :
265 : 8 : bool in(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
266 : : const char* arg_name, GjsArgumentFlags flags,
267 : : JS::HandleValue value) {
268 : 8 : return gjs_value_to_basic_array_gi_argument(
269 : : cx, value, element_tag, GI_ARRAY_TYPE_ARRAY, arg, arg_name,
270 : 8 : GJS_ARGUMENT_ARGUMENT, flags);
271 : : }
272 : :
273 : 13 : bool out(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
274 : : JS::MutableHandleValue value_out) {
275 : 13 : return gjs_value_from_basic_garray_gi_argument(cx, value_out,
276 : 13 : element_tag, arg);
277 : : }
278 : :
279 : 11 : void release_container(GIArgument* arg) {
280 [ + - ]: 11 : g_clear_pointer(&gjs_arg_member<GArray*>(arg), g_array_unref);
281 : 11 : }
282 : :
283 : 4 : void release_contents(GIArgument* arg) {
284 : 4 : GArray* array = gjs_arg_get<GArray*>(arg);
285 [ + + ]: 16 : for (size_t ix = 0; ix < array->len; ix++)
286 : 12 : g_free(g_array_index(array, char*, ix));
287 : 4 : }
288 : :
289 : 6 : Maybe<ReturnTag> return_tag() const {
290 : 6 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
291 : : }
292 : : };
293 : :
294 : : struct GPtrArrayContainer {
295 : 14 : constexpr explicit GPtrArrayContainer(const GI::TypeInfo) {}
296 : :
297 : 3 : bool in(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
298 : : const char* arg_name, GjsArgumentFlags flags,
299 : : JS::HandleValue value) {
300 : 3 : return gjs_value_to_basic_array_gi_argument(
301 : : cx, value, element_tag, GI_ARRAY_TYPE_PTR_ARRAY, arg, arg_name,
302 : 3 : GJS_ARGUMENT_ARGUMENT, flags);
303 : : }
304 : :
305 : 12 : bool out(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
306 : : JS::MutableHandleValue value_out) {
307 : 12 : return gjs_value_from_basic_gptrarray_gi_argument(cx, value_out,
308 : 12 : element_tag, arg);
309 : : }
310 : :
311 : 8 : void release_container(GIArgument* arg) {
312 [ + - ]: 8 : g_clear_pointer(&gjs_arg_member<GPtrArray*>(arg), g_ptr_array_unref);
313 : 8 : }
314 : :
315 : 5 : void release_contents(GIArgument* arg) {
316 : 5 : GPtrArray* array = gjs_arg_get<GPtrArray*>(arg);
317 : 5 : g_ptr_array_foreach(
318 : 13 : array, [](void* ptr, void*) { g_free(ptr); }, nullptr);
319 : 5 : }
320 : :
321 : 5 : Maybe<ReturnTag> return_tag() const {
322 : 5 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
323 : : }
324 : : };
325 : :
326 : : struct FixedSizeArray {
327 : 26 : explicit FixedSizeArray(const GI::TypeInfo type_info) {
328 : 26 : size_t fixed_size = type_info.array_fixed_size().value();
329 : 26 : g_assert(
330 : : fixed_size <= UINT32_MAX &&
331 : : "4294967295 fixed array elements ought to be enough for anybody");
332 : 26 : m_fixed_size = fixed_size;
333 : 26 : }
334 : :
335 : : uint32_t m_fixed_size = -1;
336 : :
337 : 10 : bool in(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
338 : : const char* arg_name, GjsArgumentFlags flags,
339 : : JS::HandleValue value) {
340 : 10 : return gjs_value_to_basic_array_gi_argument(
341 : : cx, value, element_tag, GI_ARRAY_TYPE_C, arg, arg_name,
342 : 10 : GJS_ARGUMENT_ARGUMENT, flags);
343 : : }
344 : :
345 : 17 : bool out(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
346 : : JS::MutableHandleValue value) {
347 : 17 : return gjs_value_from_basic_fixed_size_array_gi_argument(
348 : 17 : cx, value, element_tag, m_fixed_size, arg);
349 : : }
350 : :
351 : 15 : void release_container(GIArgument* arg) {
352 [ + - ]: 15 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
353 : 15 : }
354 : :
355 : 5 : void release_contents(GIArgument* arg) {
356 : 5 : char** array = gjs_arg_get<char**>(arg);
357 [ + + ]: 25 : for (size_t ix = 0; ix < m_fixed_size; ix++)
358 : 20 : g_free(array[ix]);
359 : 5 : }
360 : :
361 : 7 : Maybe<ReturnTag> return_tag() const {
362 : 7 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
363 : : }
364 : : };
365 : :
366 : : struct GListContainer {
367 : 50 : explicit GListContainer(const GI::TypeInfo type_info)
368 : 50 : : m_double_link(type_info.tag() == GI_TYPE_TAG_GLIST) {}
369 : : bool m_double_link : 1;
370 : :
371 : 16 : bool in(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
372 : : const char* arg_name, GjsArgumentFlags, JS::HandleValue value) {
373 [ + + ]: 16 : if (m_double_link) {
374 : 8 : return gjs_value_to_basic_glist_gi_argument(
375 : 8 : cx, value, element_tag, arg, arg_name, GJS_ARGUMENT_ARGUMENT);
376 : : }
377 : 8 : return gjs_value_to_basic_gslist_gi_argument(
378 : 8 : cx, value, element_tag, arg, arg_name, GJS_ARGUMENT_ARGUMENT);
379 : : }
380 : :
381 : 36 : bool out(JSContext* cx, GITypeTag element_tag, GIArgument* arg,
382 : : JS::MutableHandleValue value) {
383 [ + + ]: 36 : if (m_double_link)
384 : 17 : return gjs_array_from_basic_glist_gi_argument(cx, value,
385 : 17 : element_tag, arg);
386 : 19 : return gjs_array_from_basic_gslist_gi_argument(cx, value, element_tag,
387 : 19 : arg);
388 : : }
389 : :
390 : 25 : void release_container(GIArgument* arg) {
391 [ + + ]: 25 : if (m_double_link)
392 [ + - ]: 13 : g_clear_pointer(&gjs_arg_member<GList*>(arg), g_list_free);
393 : : else
394 [ + - ]: 12 : g_clear_pointer(&gjs_arg_member<GSList*>(arg), g_slist_free);
395 : 25 : }
396 : :
397 : 16 : void release_contents(GIArgument* arg) {
398 : 46 : GFunc free_gfunc = [](void* data, void*) { g_free(data); };
399 : :
400 [ + + ]: 16 : if (m_double_link) {
401 : 8 : GList* list = gjs_arg_get<GList*>(arg);
402 : 8 : g_list_foreach(list, free_gfunc, nullptr);
403 : : } else {
404 : 8 : GSList* list = gjs_arg_get<GSList*>(arg);
405 : 8 : g_slist_foreach(list, free_gfunc, nullptr);
406 : : }
407 : 16 : }
408 : :
409 : 20 : Maybe<ReturnTag> return_tag() const {
410 : 20 : return Some(ReturnTag{container_tag()});
411 : : }
412 : 20 : constexpr GITypeTag container_tag() const {
413 [ + + ]: 20 : return m_double_link ? GI_TYPE_TAG_GLIST : GI_TYPE_TAG_GSLIST;
414 : : }
415 : : };
416 : :
417 : : struct GHashContainer {
418 : 31 : explicit GHashContainer(const GI::TypeInfo type_info)
419 : 31 : : m_value_tag(type_info.value_type().tag()) {}
420 : : constexpr GITypeTag value_tag() const { return m_value_tag; }
421 : :
422 : : // Key type is managed by the basic container
423 : : GITypeTag m_value_tag;
424 : : };
425 : :
426 : : template <GI::InfoTag TAG>
427 : : struct HasIntrospectionInfo {
428 : 1557 : constexpr explicit HasIntrospectionInfo(const GI::UnownedInfo<TAG>& info)
429 : 1557 : : m_info(info) {}
430 : :
431 : : GI::OwnedInfo<TAG> m_info;
432 : : };
433 : :
434 : : // boxed / union / GObject
435 : : struct GTypedType {
436 : 7242 : explicit GTypedType(GType gtype) : m_gtype(gtype) {}
437 : 109138 : constexpr GType gtype() const { return m_gtype; }
438 : :
439 : : protected:
440 : : GType m_gtype;
441 : : };
442 : :
443 : : struct RegisteredType : GTypedType {
444 : 8 : RegisteredType(GType gtype, GIInfoType info_type)
445 : 8 : : GTypedType(gtype), m_info_type(info_type) {}
446 : 6735 : explicit RegisteredType(const GI::RegisteredTypeInfo info)
447 : 6735 : : GTypedType(info.gtype()), m_info_type(info.type()) {
448 : 6735 : g_assert(m_gtype != G_TYPE_NONE &&
449 : : "Use RegisteredInterface for this type");
450 : 6735 : }
451 : :
452 : : Maybe<ReturnTag> return_tag() const {
453 : : return Some(ReturnTag{GI_TYPE_TAG_INTERFACE, m_info_type, true});
454 : : }
455 : :
456 : : GIInfoType m_info_type : 5;
457 : : };
458 : :
459 : : template <GI::InfoTag TAG>
460 : : struct RegisteredInterface : HasIntrospectionInfo<TAG>, GTypedType {
461 : 489 : explicit RegisteredInterface(const GI::UnownedInfo<TAG> info)
462 : 489 : : HasIntrospectionInfo<TAG>(info), GTypedType(info.gtype()) {}
463 : :
464 : : Maybe<ReturnTag> return_tag() const {
465 : : return Some(ReturnTag{GI_TYPE_TAG_INTERFACE,
466 : : HasIntrospectionInfo<TAG>::m_info.type(), true});
467 : : }
468 : : };
469 : :
470 : : struct Callback : Nullable, HasIntrospectionInfo<GI::InfoTag::CALLBACK> {
471 : 1060 : explicit Callback(const GI::CallbackInfo info, Maybe<unsigned> closure_pos,
472 : : Maybe<unsigned> destroy_pos, GIScopeType scope)
473 : 1060 : : HasIntrospectionInfo(info),
474 : 1060 : m_closure_pos(closure_pos.valueOr(Argument::ABSENT)),
475 : 1060 : m_destroy_pos(destroy_pos.valueOr(Argument::ABSENT)),
476 : 1060 : m_scope(scope) {
477 : 1060 : g_assert(destroy_pos.valueOr(0) <= Argument::MAX_ARGS &&
478 : : "No more than 253 arguments allowed");
479 : 1060 : g_assert(closure_pos.valueOr(0) <= Argument::MAX_ARGS &&
480 : : "No more than 253 arguments allowed");
481 : 1060 : }
482 : :
483 : 520 : [[nodiscard]] constexpr bool has_callback_destroy() {
484 : 520 : return m_destroy_pos != Argument::ABSENT;
485 : : }
486 : :
487 : 260 : [[nodiscard]] constexpr bool has_callback_closure() {
488 : 260 : return m_closure_pos != Argument::ABSENT;
489 : : }
490 : :
491 : : uint8_t m_closure_pos;
492 : : uint8_t m_destroy_pos;
493 : : GIScopeType m_scope : 3;
494 : : };
495 : :
496 : : struct Enum {
497 : : explicit Enum(const GI::EnumInfo);
498 : : bool m_unsigned : 1;
499 : : uint32_t m_min = 0;
500 : : uint32_t m_max = 0;
501 : : };
502 : :
503 : : struct Flags {
504 : : explicit Flags(const GI::FlagsInfo);
505 : : unsigned m_mask = 0;
506 : : };
507 : :
508 : : struct CallerAllocates {
509 : 16 : explicit CallerAllocates(size_t size) : m_allocates_size(size) {}
510 : : size_t m_allocates_size;
511 : : };
512 : :
513 : : // Gjs::Arguments:
514 : : //
515 : : // Each argument, irrespective of the direction, is processed in three phases:
516 : : // - before calling the function [in]
517 : : // - after calling it, when converting the return value and out arguments [out]
518 : : // - at the end of the invocation, to release any allocated memory [release]
519 : : //
520 : : // Some types don't have direction (for example, caller_allocates is only out,
521 : : // and callback is only in), in which case it is implied.
522 : :
523 : : struct SkipAll : Argument {
524 : 699 : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
525 : : JS::HandleValue) override {
526 : 699 : return skip();
527 : : }
528 : :
529 : 80659 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
530 : : JS::MutableHandleValue) override {
531 : 80659 : return skip();
532 : : }
533 : :
534 : 50801 : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
535 : : GIArgument*) override {
536 : 50801 : return skip();
537 : : }
538 : :
539 : : protected:
540 : 200417 : constexpr bool skip() { return true; }
541 : : };
542 : :
543 : : struct Fallback : Transferable, HasTypeInfo {
544 : : using HasTypeInfo::HasTypeInfo;
545 : : };
546 : :
547 : : struct FallbackIn : SkipAll, Fallback, Nullable {
548 : : using Fallback::Fallback;
549 : :
550 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
551 : : JS::HandleValue) override;
552 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
553 : : GIArgument*) override;
554 : :
555 : 5 : GjsArgumentFlags flags() const override {
556 : 5 : return Argument::flags() | Nullable::flags();
557 : : }
558 : : };
559 : :
560 : : struct FallbackInOut : SkipAll, Positioned, Fallback {
561 : : using Fallback::Fallback;
562 : :
563 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
564 : : JS::HandleValue) override;
565 : : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
566 : : JS::MutableHandleValue) override;
567 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
568 : : GIArgument*) override;
569 : : };
570 : :
571 : : struct FallbackOut : FallbackInOut {
572 : : using FallbackInOut::FallbackInOut;
573 : :
574 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
575 : : JS::HandleValue) override;
576 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
577 : : GIArgument*) override;
578 : : };
579 : :
580 : : struct FallbackReturn : FallbackOut {
581 : : using FallbackOut::FallbackOut;
582 : : // No in!
583 : 0 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
584 : : JS::HandleValue) override {
585 : 0 : return invalid(cx, G_STRFUNC);
586 : : }
587 : :
588 : 21580 : Maybe<ReturnTag> return_tag() const override {
589 : 21580 : return HasTypeInfo::return_tag();
590 : : }
591 : : };
592 : :
593 : : template <typename TAG>
594 : : struct NumericOut : SkipAll, Positioned {
595 : : static_assert(std::is_arithmetic_v<Tag::RealT<TAG>>, "Not arithmetic type");
596 : 546 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
597 : : JS::HandleValue) override {
598 : 546 : return set_out_parameter(state, arg);
599 : : }
600 : 546 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
601 : : JS::MutableHandleValue value) override {
602 : 546 : return Gjs::c_value_to_js_checked<TAG>(cx, gjs_arg_get<TAG>(arg),
603 : 546 : value);
604 : : }
605 : : };
606 : :
607 : : using BooleanOut = NumericOut<Tag::GBoolean>;
608 : :
609 : : template <typename TAG>
610 : : struct NumericReturn : SkipAll {
611 : : static_assert(std::is_arithmetic_v<Tag::RealT<TAG>>, "Not arithmetic type");
612 : 0 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
613 : : JS::HandleValue) override {
614 : 0 : return invalid(cx, G_STRFUNC);
615 : : }
616 : 15725 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
617 : : JS::MutableHandleValue value) override {
618 : 15725 : return Gjs::c_value_to_js_checked<TAG>(cx, gjs_arg_get<TAG>(arg),
619 : 15725 : value);
620 : : }
621 : 15725 : Maybe<ReturnTag> return_tag() const override {
622 : 15725 : return Some(ReturnTag{MarshallingInfo<TAG>::gi_tag});
623 : : }
624 : : };
625 : :
626 : : using BooleanReturn = NumericReturn<Tag::GBoolean>;
627 : :
628 : : struct SimpleOut : SkipAll, Positioned {
629 : 39 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
630 : : JS::HandleValue) override {
631 : 39 : return set_out_parameter(state, arg);
632 : : };
633 : : };
634 : :
635 : : struct BasicTypeReturn : SkipAll, BasicType {
636 : : using BasicType::BasicType;
637 : :
638 : 0 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
639 : : JS::HandleValue) override {
640 : 0 : return invalid(cx, G_STRFUNC);
641 : : }
642 : 1539 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
643 : : JS::MutableHandleValue value) override {
644 : 1539 : return gjs_value_from_basic_gi_argument(cx, value, m_tag, arg);
645 : : }
646 : 1516 : bool release(JSContext*, GjsFunctionCallState*,
647 : : GIArgument* in_arg [[maybe_unused]],
648 : : GIArgument* out_arg) override {
649 : 1516 : gjs_gi_argument_release_basic(GI_TRANSFER_NOTHING, m_tag,
650 : : Argument::flags(), out_arg);
651 : 1516 : return true;
652 : : }
653 : :
654 : 1524 : Maybe<ReturnTag> return_tag() const override {
655 : 1524 : return Some(ReturnTag{m_tag});
656 : : }
657 : : };
658 : :
659 : : struct BasicTypeOut : BasicTypeReturn, Positioned {
660 : : using BasicTypeReturn::BasicTypeReturn;
661 : :
662 : 0 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
663 : : JS::HandleValue) override {
664 : 0 : return set_out_parameter(state, arg);
665 : : }
666 : : };
667 : :
668 : : struct BasicTypeInOut : BasicTypeOut {
669 : : using BasicTypeOut::BasicTypeOut;
670 : 4 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
671 : : JS::HandleValue value) override {
672 [ - + ]: 4 : if (!gjs_value_to_basic_gi_argument(cx, value, m_tag, arg, arg_name(),
673 : 4 : GJS_ARGUMENT_ARGUMENT, flags()))
674 : 0 : return false;
675 : :
676 : 4 : return set_inout_parameter(state, arg);
677 : : }
678 : 4 : bool release(JSContext*, GjsFunctionCallState* state,
679 : : GIArgument* in_arg [[maybe_unused]],
680 : : GIArgument* out_arg [[maybe_unused]]) override {
681 : : GIArgument* original_out_arg =
682 : 4 : &(state->inout_original_cvalue(m_arg_pos));
683 : 4 : gjs_gi_argument_release_basic(GI_TRANSFER_NOTHING, m_tag, flags(),
684 : : original_out_arg);
685 : 4 : return true;
686 : : }
687 : : };
688 : :
689 : : struct BasicTypeTransferableReturn : BasicTypeReturn, Transferable {
690 : : using BasicTypeReturn::BasicTypeReturn;
691 : 19 : bool release(JSContext*, GjsFunctionCallState*,
692 : : GIArgument* in_arg [[maybe_unused]],
693 : : GIArgument* out_arg) override {
694 : 19 : gjs_gi_argument_release_basic(m_transfer, m_tag, Argument::flags(),
695 : : out_arg);
696 : 19 : return true;
697 : : }
698 : : };
699 : :
700 : : struct BasicTypeTransferableOut : BasicTypeTransferableReturn, Positioned {
701 : : using BasicTypeTransferableReturn::BasicTypeTransferableReturn;
702 : :
703 : 11 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
704 : : JS::HandleValue) override {
705 : 11 : return set_out_parameter(state, arg);
706 : : }
707 : : };
708 : :
709 : : struct BasicTypeTransferableInOut : BasicTypeInOut, Transferable {
710 : : using BasicTypeInOut::BasicTypeInOut;
711 : :
712 : 3 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
713 : : GIArgument* out_arg) override {
714 [ - + ]: 3 : if (!BasicTypeInOut::release(cx, state, in_arg, out_arg))
715 : 0 : return false;
716 : :
717 [ + - ]: 3 : if (m_transfer != GI_TRANSFER_NOTHING)
718 : 3 : gjs_gi_argument_release_basic(m_transfer, m_tag, flags(), out_arg);
719 : :
720 : 3 : return true;
721 : : }
722 : : };
723 : :
724 : : struct ErrorIn : SkipAll, Transferable, Nullable {
725 : 4 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
726 : : JS::HandleValue value) override {
727 : 4 : return gjs_value_to_gerror_gi_argument(cx, value, m_transfer, arg,
728 : : m_arg_name,
729 : 8 : GJS_ARGUMENT_ARGUMENT, flags());
730 : : }
731 : :
732 : 4 : GjsArgumentFlags flags() const override {
733 : 4 : return Argument::flags() | Nullable::flags();
734 : : }
735 : : };
736 : :
737 : : // The tag is normally used in containers for the element type, but for explicit
738 : : // arrays we use it for the length argument type. Specific implementations can
739 : : // override this
740 : : struct BasicTypeContainerReturn : BasicTypeTransferableReturn, Nullable {
741 : 1563 : explicit BasicTypeContainerReturn(GITypeTag element_tag)
742 : 1563 : : BasicTypeTransferableReturn(element_tag) {}
743 : :
744 : 448 : explicit BasicTypeContainerReturn(const GI::TypeInfo type_info)
745 : 448 : : BasicTypeContainerReturn(type_info.element_type().tag()) {}
746 : :
747 : 428 : GjsArgumentFlags flags() const override {
748 : 428 : return Argument::flags() | Nullable::flags();
749 : : }
750 : 0 : Maybe<ReturnTag> return_tag() const override {
751 : : // in Return subclasses, this must be overridden with the container tag
752 : : g_return_val_if_reached(Nothing{});
753 : : }
754 : 352 : constexpr GITypeTag element_tag() { return m_tag; }
755 : : };
756 : :
757 : : struct BasicTypeContainerOut : BasicTypeContainerReturn, Positioned {
758 : : using BasicTypeContainerReturn::BasicTypeContainerReturn;
759 : : };
760 : :
761 : : struct BasicTypeContainerIn : BasicTypeContainerReturn {
762 : : using BasicTypeContainerReturn::BasicTypeContainerReturn;
763 : :
764 : 0 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
765 : : JS::MutableHandleValue) override {
766 : 0 : return skip();
767 : : }
768 : : };
769 : :
770 : : struct BasicTypeContainerInOut : BasicTypeContainerOut {
771 : : using BasicTypeContainerOut::BasicTypeContainerOut;
772 : : };
773 : :
774 : : template <class Marshaller, class Container>
775 : : struct BasicTypeContainer : Marshaller, Container {
776 : 448 : explicit BasicTypeContainer(const GI::TypeInfo type_info)
777 : 448 : : Marshaller(type_info), Container(type_info) {}
778 : :
779 : 107 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
780 : : JS::HandleValue value) override {
781 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerIn>) {
782 : 51 : return Container::in(cx, Marshaller::element_tag(), arg,
783 : : Marshaller::arg_name(), Marshaller::flags(),
784 : 51 : value);
785 : : }
786 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerReturn>)
787 : 0 : return Marshaller::invalid(cx, G_STRFUNC);
788 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerOut>)
789 : 44 : return Marshaller::set_out_parameter(state, arg);
790 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerInOut>) {
791 : 12 : return Container::in(cx, Marshaller::element_tag(), arg,
792 : : Marshaller::arg_name(), Marshaller::flags(),
793 [ + - + - ]: 24 : value) &&
794 : 24 : Marshaller::set_inout_parameter(state, arg);
795 : : }
796 : : g_return_val_if_reached(false);
797 : : }
798 : :
799 : 168 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
800 : : JS::MutableHandleValue value) override {
801 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerIn>)
802 : 47 : return Marshaller::skip();
803 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerReturn> ||
804 : : std::is_same_v<Marshaller, BasicTypeContainerOut> ||
805 : : std::is_same_v<Marshaller, BasicTypeContainerInOut>) {
806 : 121 : return Container::out(cx, Marshaller::element_tag(), arg, value);
807 : : }
808 : : g_return_val_if_reached(false);
809 : : }
810 : 168 : bool release(JSContext*, GjsFunctionCallState* state, GIArgument* in_arg,
811 : : GIArgument* out_arg) override {
812 : 168 : GITransfer transfer = Marshaller::m_transfer;
813 : 168 : GITypeTag element_tag = Marshaller::element_tag();
814 : :
815 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerIn>) {
816 [ + + ]: 47 : if (!state->call_completed())
817 : 1 : transfer = GI_TRANSFER_NOTHING;
818 : :
819 [ + + + + : 47 : if (!gjs_arg_get<void*>(in_arg) ||
+ + ]
820 : : transfer == GI_TRANSFER_EVERYTHING)
821 : 9 : return true;
822 : :
823 [ + + ]: 38 : if (Gjs::basic_type_needs_release(element_tag))
824 : 23 : Container::release_contents(in_arg);
825 [ + + ]: 38 : if (transfer != GI_TRANSFER_CONTAINER)
826 : 37 : Container::release_container(in_arg);
827 : :
828 : 38 : return true;
829 : : }
830 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerInOut>) {
831 [ - + ]: 12 : if (!state->call_completed())
832 : 0 : transfer = GI_TRANSFER_NOTHING;
833 : :
834 : : GIArgument* original_out_arg =
835 : 12 : &(state->inout_original_cvalue(Marshaller::m_arg_pos));
836 : 12 : void* original_out_ptr = gjs_arg_get<void*>(original_out_arg);
837 [ + + ]: 12 : if (original_out_ptr &&
838 [ + - + + : 24 : original_out_ptr != gjs_arg_get<void*>(out_arg) &&
+ + ]
839 : : transfer != GI_TRANSFER_EVERYTHING) {
840 [ + + ]: 9 : if (Gjs::basic_type_needs_release(element_tag))
841 : 8 : Container::release_contents(original_out_arg);
842 [ + - ]: 9 : if (transfer != GI_TRANSFER_CONTAINER)
843 : 9 : Container::release_container(original_out_arg);
844 : : }
845 : : }
846 : : if constexpr (std::is_same_v<Marshaller, BasicTypeContainerReturn> ||
847 : : std::is_same_v<Marshaller, BasicTypeContainerOut> ||
848 : : std::is_same_v<Marshaller, BasicTypeContainerInOut>) {
849 [ + + + + : 121 : if (!gjs_arg_get<void*>(out_arg) || transfer == GI_TRANSFER_NOTHING)
+ + ]
850 : 63 : return true;
851 : :
852 [ + + + + : 58 : if (Gjs::basic_type_needs_release(element_tag) &&
+ + ]
853 : : transfer != GI_TRANSFER_CONTAINER)
854 : 37 : Container::release_contents(out_arg);
855 : 58 : Container::release_container(out_arg);
856 : :
857 : 58 : return true;
858 : : }
859 : : g_return_val_if_reached(false);
860 : : }
861 : :
862 : 65 : Maybe<ReturnTag> return_tag() const override {
863 : 65 : return Container::return_tag();
864 : : }
865 : : };
866 : :
867 : : using BasicGListReturn =
868 : : BasicTypeContainer<BasicTypeContainerReturn, GListContainer>;
869 : : using BasicGListIn = BasicTypeContainer<BasicTypeContainerIn, GListContainer>;
870 : : using BasicGListOut = BasicTypeContainer<BasicTypeContainerOut, GListContainer>;
871 : : using BasicGListInOut =
872 : : BasicTypeContainer<BasicTypeContainerInOut, GListContainer>;
873 : :
874 : : using BasicCZeroTerminatedArrayReturn =
875 : : BasicTypeContainer<BasicTypeContainerReturn, ZeroTerminatedArray>;
876 : : using BasicCZeroTerminatedArrayIn =
877 : : BasicTypeContainer<BasicTypeContainerIn, ZeroTerminatedArray>;
878 : : using BasicCZeroTerminatedArrayOut =
879 : : BasicTypeContainer<BasicTypeContainerOut, ZeroTerminatedArray>;
880 : : using BasicCZeroTerminatedArrayInOut =
881 : : BasicTypeContainer<BasicTypeContainerInOut, ZeroTerminatedArray>;
882 : :
883 : : using BasicCFixedSizeArrayReturn =
884 : : BasicTypeContainer<BasicTypeContainerReturn, FixedSizeArray>;
885 : : using BasicCFixedSizeArrayIn =
886 : : BasicTypeContainer<BasicTypeContainerIn, FixedSizeArray>;
887 : : using BasicCFixedSizeArrayOut =
888 : : BasicTypeContainer<BasicTypeContainerOut, FixedSizeArray>;
889 : : using BasicCFixedSizeArrayInOut =
890 : : BasicTypeContainer<BasicTypeContainerInOut, FixedSizeArray>;
891 : :
892 : : using BasicGArrayReturn =
893 : : BasicTypeContainer<BasicTypeContainerReturn, GArrayContainer>;
894 : : using BasicGArrayIn = BasicTypeContainer<BasicTypeContainerIn, GArrayContainer>;
895 : : using BasicGArrayOut =
896 : : BasicTypeContainer<BasicTypeContainerOut, GArrayContainer>;
897 : : using BasicGArrayInOut =
898 : : BasicTypeContainer<BasicTypeContainerInOut, GArrayContainer>;
899 : :
900 : : using BasicGPtrArrayReturn =
901 : : BasicTypeContainer<BasicTypeContainerReturn, GPtrArrayContainer>;
902 : : using BasicGPtrArrayIn =
903 : : BasicTypeContainer<BasicTypeContainerIn, GPtrArrayContainer>;
904 : : using BasicGPtrArrayOut =
905 : : BasicTypeContainer<BasicTypeContainerOut, GPtrArrayContainer>;
906 : : using BasicGPtrArrayInOut =
907 : : BasicTypeContainer<BasicTypeContainerInOut, GPtrArrayContainer>;
908 : :
909 : : struct BasicGHashReturn : BasicTypeTransferableReturn,
910 : : GHashContainer,
911 : : Nullable {
912 : 31 : explicit BasicGHashReturn(const GI::TypeInfo type_info)
913 : 31 : : BasicTypeTransferableReturn(type_info.key_type().tag()),
914 : 62 : GHashContainer(type_info) {
915 : 31 : g_assert(GI_TYPE_TAG_IS_BASIC(m_value_tag));
916 : 31 : }
917 : :
918 : 19 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
919 : : JS::MutableHandleValue value) override {
920 : 19 : return gjs_value_from_basic_ghash(cx, value, m_tag, m_value_tag,
921 : 19 : gjs_arg_get<GHashTable*>(arg));
922 : : }
923 : 18 : bool release(JSContext*, GjsFunctionCallState*,
924 : : GIArgument* in_arg [[maybe_unused]],
925 : : GIArgument* out_arg) override {
926 [ + + ]: 18 : if (m_transfer == GI_TRANSFER_NOTHING)
927 : 7 : return true;
928 : :
929 : : gjs_debug_marshal(
930 : : GJS_DEBUG_GFUNCTION,
931 : : "Releasing GIArgument ghash out param or return value");
932 : :
933 : 11 : gjs_gi_argument_release_basic_ghash(m_transfer, m_tag, m_value_tag,
934 : : out_arg);
935 : 11 : return true;
936 : : }
937 : :
938 : 13 : GjsArgumentFlags flags() const override {
939 : 13 : return Argument::flags() | Nullable::flags();
940 : : }
941 : 11 : Maybe<ReturnTag> return_tag() const override {
942 : 11 : return Some(ReturnTag{GI_TYPE_TAG_GHASH});
943 : : }
944 : : };
945 : :
946 : : struct BasicGHashIn : BasicGHashReturn {
947 : : using BasicGHashReturn::BasicGHashReturn;
948 : :
949 : 12 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
950 : : JS::HandleValue value) override {
951 : 24 : return gjs_value_to_basic_ghash_gi_argument(
952 : 12 : cx, value, m_tag, m_value_tag, m_transfer, arg, m_arg_name,
953 : 24 : GJS_ARGUMENT_ARGUMENT, flags());
954 : : }
955 : 12 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
956 : : JS::MutableHandleValue) override {
957 : 12 : return skip();
958 : : }
959 : 12 : bool release(JSContext*, GjsFunctionCallState* state, GIArgument* in_arg,
960 : : GIArgument* out_arg [[maybe_unused]]) override {
961 : : // GI_TRANSFER_EVERYTHING: we don't own the argument anymore.
962 : : // GI_TRANSFER_CONTAINER: See FIXME in gjs_array_to_g_list(); currently
963 : : // an error and we won't get here.
964 [ + - + + : 12 : if (!state->call_completed() || m_transfer != GI_TRANSFER_NOTHING)
+ + ]
965 : 1 : return true;
966 : :
967 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
968 : : "Releasing GIArgument ghash in param");
969 : :
970 : 11 : gjs_gi_argument_release_basic_ghash(m_transfer, m_tag, m_value_tag,
971 : : in_arg);
972 : 11 : return true;
973 : : }
974 : : };
975 : :
976 : : struct BasicGHashOut : BasicGHashReturn, Positioned {
977 : : using BasicGHashReturn::BasicGHashReturn;
978 : :
979 : 7 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
980 : : JS::HandleValue) override {
981 : 7 : return set_out_parameter(state, arg);
982 : : }
983 : : };
984 : :
985 : : struct BasicGHashInOut : BasicGHashOut {
986 : : using BasicGHashOut::BasicGHashOut;
987 : 1 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
988 : : JS::HandleValue value) override {
989 [ - + ]: 1 : if (!gjs_value_to_basic_ghash_gi_argument(
990 : 1 : cx, value, m_tag, m_value_tag, m_transfer, arg, m_arg_name,
991 : 1 : GJS_ARGUMENT_ARGUMENT, flags()))
992 : 0 : return false;
993 : :
994 : 1 : return set_inout_parameter(state, arg);
995 : : }
996 : 1 : bool release(JSContext*, GjsFunctionCallState* state,
997 : : GIArgument* in_arg [[maybe_unused]],
998 : : GIArgument* out_arg) override {
999 : : GIArgument* original_out_arg =
1000 : 1 : &(state->inout_original_cvalue(m_arg_pos));
1001 : 1 : gjs_gi_argument_release_basic_ghash(GI_TRANSFER_NOTHING, m_tag,
1002 : : m_value_tag, original_out_arg);
1003 [ - + ]: 1 : if (m_transfer != GI_TRANSFER_NOTHING)
1004 : 0 : gjs_gi_argument_release_basic_ghash(m_transfer, m_tag, m_value_tag,
1005 : : out_arg);
1006 : 1 : return true;
1007 : : }
1008 : : };
1009 : :
1010 : : struct ByteArrayReturn : SkipAll, Transferable {
1011 : 0 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
1012 : : JS::HandleValue) override {
1013 : 0 : return invalid(cx, G_STRFUNC);
1014 : : }
1015 : 4 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1016 : : JS::MutableHandleValue value) override {
1017 : 4 : return gjs_value_from_byte_array_gi_argument(cx, value, arg);
1018 : : }
1019 : 3 : bool release(JSContext*, GjsFunctionCallState*,
1020 : : GIArgument* in_arg [[maybe_unused]],
1021 : : GIArgument* out_arg) override {
1022 [ + - ]: 3 : if (m_transfer != GI_TRANSFER_NOTHING)
1023 : 3 : gjs_gi_argument_release_byte_array(out_arg);
1024 : 3 : return true;
1025 : : }
1026 : :
1027 : 2 : Maybe<ReturnTag> return_tag() const override {
1028 : 2 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
1029 : : }
1030 : : };
1031 : :
1032 : : struct ByteArrayIn : SkipAll, Transferable {
1033 : 3 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1034 : : JS::HandleValue value) override {
1035 : 3 : return gjs_value_to_byte_array_gi_argument(cx, value, arg, arg_name(),
1036 : 6 : flags());
1037 : : }
1038 : 3 : bool release(JSContext*, GjsFunctionCallState* state, GIArgument* in_arg,
1039 : : GIArgument* out_arg [[maybe_unused]]) override {
1040 [ + - - + : 3 : if (!state->call_completed() || m_transfer != GI_TRANSFER_NOTHING)
- + ]
1041 : 0 : return true;
1042 : :
1043 : 3 : gjs_gi_argument_release_byte_array(in_arg);
1044 : 3 : return true;
1045 : : }
1046 : : };
1047 : :
1048 : : struct ByteArrayOut : ByteArrayReturn, Positioned {
1049 : : using ByteArrayReturn::ByteArrayReturn;
1050 : :
1051 : 1 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
1052 : : JS::HandleValue) override {
1053 : 1 : return set_out_parameter(state, arg);
1054 : : }
1055 : : };
1056 : :
1057 : : struct ByteArrayInOut : ByteArrayOut {
1058 : : using ByteArrayOut::ByteArrayOut;
1059 : 1 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1060 : : JS::HandleValue value) override {
1061 : 1 : return gjs_value_to_byte_array_gi_argument(cx, value, arg, m_arg_name,
1062 [ + - + - ]: 3 : flags()) &&
1063 : 2 : set_inout_parameter(state, arg);
1064 : : }
1065 : 1 : bool release(JSContext*, GjsFunctionCallState* state,
1066 : : GIArgument* in_arg [[maybe_unused]],
1067 : : GIArgument* out_arg) override {
1068 : : GIArgument* original_out_arg =
1069 : 1 : &(state->inout_original_cvalue(m_arg_pos));
1070 [ - + ]: 1 : if (m_transfer != GI_TRANSFER_EVERYTHING)
1071 : 0 : gjs_gi_argument_release_byte_array(original_out_arg);
1072 [ + - ]: 1 : if (m_transfer != GI_TRANSFER_NOTHING)
1073 : 1 : gjs_gi_argument_release_byte_array(out_arg);
1074 : 1 : return true;
1075 : : }
1076 : : };
1077 : :
1078 : : struct ExplicitArrayBase : BasicTypeContainerReturn, ExplicitArray {
1079 : 1115 : ExplicitArrayBase(unsigned length_pos, GITypeTag length_tag,
1080 : : GIDirection length_direction)
1081 : 1115 : : BasicTypeContainerReturn(length_tag),
1082 : 1115 : ExplicitArray(length_pos, length_direction) {}
1083 : : };
1084 : :
1085 : : struct CArrayIn : ExplicitArrayBase, HasTypeInfo {
1086 : 322 : CArrayIn(const GI::TypeInfo type_info, unsigned length_pos,
1087 : : GITypeTag length_tag, GIDirection length_direction)
1088 : 322 : : ExplicitArrayBase(length_pos, length_tag, length_direction),
1089 : 322 : HasTypeInfo(type_info) {}
1090 : :
1091 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1092 : : JS::HandleValue) override;
1093 : 244 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
1094 : : JS::MutableHandleValue) override {
1095 : 244 : return skip();
1096 : : };
1097 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1098 : : GIArgument*) override;
1099 : : };
1100 : :
1101 : : // Positioned must come before HasTypeInfo for struct packing reasons, otherwise
1102 : : // this could inherit from CArrayIn
1103 : : struct CArrayInOut : ExplicitArrayBase, Positioned, HasTypeInfo {
1104 : 149 : CArrayInOut(const GI::TypeInfo type_info, unsigned length_pos,
1105 : : GITypeTag length_tag, GIDirection length_direction)
1106 : 149 : : ExplicitArrayBase(length_pos, length_tag, length_direction),
1107 : 149 : HasTypeInfo(type_info) {}
1108 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1109 : : JS::HandleValue) override;
1110 : : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
1111 : : JS::MutableHandleValue) override;
1112 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1113 : : GIArgument*) override;
1114 : : };
1115 : :
1116 : : struct CArrayOut : CArrayInOut {
1117 : : using CArrayInOut::CArrayInOut;
1118 : :
1119 : 6 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1120 : : JS::HandleValue) override {
1121 [ - + ]: 6 : if (m_length_direction != GI_DIRECTION_OUT) {
1122 : 0 : gjs_throw(cx,
1123 : : "Using different length argument direction for array %s"
1124 : : "is not supported for out arrays",
1125 : : m_arg_name);
1126 : 0 : return false;
1127 : : }
1128 : 6 : return set_out_parameter(state, arg);
1129 : : };
1130 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1131 : : GIArgument*) override;
1132 : :
1133 : 4 : Maybe<ReturnTag> return_tag() const override {
1134 : 4 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
1135 : : }
1136 : : };
1137 : :
1138 : : using ArrayLengthOut = SimpleOut;
1139 : :
1140 : : struct NotIntrospectable : SkipAll {
1141 : 69 : explicit NotIntrospectable(NotIntrospectableReason reason)
1142 : 69 : : m_reason(reason) {}
1143 : : NotIntrospectableReason m_reason;
1144 : :
1145 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1146 : : JS::HandleValue) override;
1147 : : };
1148 : :
1149 : : struct NullableIn : SkipAll, Nullable {
1150 : 13936 : inline bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1151 : : JS::HandleValue) override {
1152 : 13936 : return handle_nullable(cx, arg, m_arg_name);
1153 : : }
1154 : :
1155 : 136 : GjsArgumentFlags flags() const override {
1156 : 136 : return Argument::flags() | Nullable::flags();
1157 : : }
1158 : : };
1159 : :
1160 : : struct Instance : NullableIn {
1161 : : // Some calls accept null for the instance (thus we inherit from
1162 : : // NullableIn), but generally in an object oriented language it's wrong to
1163 : : // call a method on null.
1164 : : // As per this we actually default to SkipAll methods.
1165 : :
1166 : 0 : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1167 : : JS::HandleValue) override {
1168 : 0 : return skip();
1169 : : }
1170 : :
1171 : 40101 : Maybe<const Instance*> as_instance() const override { return Some(this); }
1172 : :
1173 : : // The instance GType can be useful only in few cases such as GObjects and
1174 : : // GInterfaces, so we don't store it by default, unless needed.
1175 : : // See Function's code to see where this is relevant.
1176 : 247 : virtual GType gtype() const { return G_TYPE_NONE; }
1177 : : };
1178 : :
1179 : : struct EnumIn : Instance, Enum {
1180 : : using Enum::Enum;
1181 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1182 : : JS::HandleValue) override;
1183 : : };
1184 : :
1185 : : struct FlagsIn : Instance, Flags {
1186 : : using Flags::Flags;
1187 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1188 : : JS::HandleValue) override;
1189 : : };
1190 : :
1191 : : struct RegisteredIn : Instance, RegisteredType, Transferable {
1192 : : using RegisteredType::RegisteredType;
1193 : :
1194 : 39854 : GType gtype() const override { return RegisteredType::gtype(); }
1195 : : };
1196 : :
1197 : : template <GI::InfoTag TAG>
1198 : : struct RegisteredInterfaceIn : Instance,
1199 : : RegisteredInterface<TAG>,
1200 : : Transferable {
1201 : : using RegisteredInterface<TAG>::RegisteredInterface;
1202 : :
1203 : 0 : GType gtype() const override { return RegisteredInterface<TAG>::gtype(); }
1204 : : };
1205 : :
1206 : : struct ForeignStructInstanceIn : RegisteredInterfaceIn<GI::InfoTag::STRUCT> {
1207 : : using RegisteredInterfaceIn<GI::InfoTag::STRUCT>::RegisteredInterfaceIn;
1208 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1209 : : JS::HandleValue) override;
1210 : : };
1211 : :
1212 : : struct ForeignStructIn : ForeignStructInstanceIn {
1213 : : using ForeignStructInstanceIn::ForeignStructInstanceIn;
1214 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1215 : : GIArgument*) override;
1216 : : };
1217 : :
1218 : : struct FallbackInterfaceIn
1219 : : : RegisteredInterfaceIn<GI::InfoTag::REGISTERED_TYPE> {
1220 : : using RegisteredInterfaceIn::RegisteredInterfaceIn;
1221 : :
1222 : 127 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1223 : : JS::HandleValue value) override {
1224 : 254 : return gjs_value_to_interface_gi_argument(
1225 : 127 : cx, value, m_info, m_transfer, arg, m_arg_name,
1226 : 254 : GJS_ARGUMENT_ARGUMENT, flags());
1227 : : }
1228 : : };
1229 : :
1230 : : struct GdkAtomIn : NullableIn {
1231 : 8 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1232 : : JS::HandleValue value) override {
1233 : 8 : return gjs_value_to_gdk_atom_gi_argument(cx, value, arg, m_arg_name,
1234 : 8 : GJS_ARGUMENT_ARGUMENT);
1235 : : }
1236 : : };
1237 : :
1238 : : struct BoxedInTransferNone : RegisteredIn {
1239 : : using RegisteredIn::RegisteredIn;
1240 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1241 : : JS::HandleValue) override;
1242 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1243 : : GIArgument*) override;
1244 : 53591 : virtual Maybe<const GI::BaseInfo> info() const { return {}; }
1245 : : };
1246 : :
1247 : : struct BoxedIn : BoxedInTransferNone {
1248 : : using BoxedInTransferNone::BoxedInTransferNone;
1249 : : // This is a smart argument, no release needed
1250 : 67205 : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1251 : : GIArgument*) override {
1252 : 67205 : return skip();
1253 : : }
1254 : : };
1255 : :
1256 : : struct UnregisteredBoxedIn : BoxedIn,
1257 : : HasIntrospectionInfo<GI::InfoTag::STRUCT> {
1258 : 8 : explicit UnregisteredBoxedIn(const GI::StructInfo info)
1259 : 8 : : BoxedIn(info.gtype(), info.type()), HasIntrospectionInfo(info) {}
1260 : : // This is a smart argument, no release needed
1261 : : };
1262 : :
1263 : : struct GValueIn : BoxedIn {
1264 : : using BoxedIn::BoxedIn;
1265 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1266 : : JS::HandleValue) override;
1267 : : };
1268 : :
1269 : : struct GValueInTransferNone : GValueIn {
1270 : : using GValueIn::GValueIn;
1271 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1272 : : GIArgument*) override;
1273 : : };
1274 : :
1275 : : struct GClosureInTransferNone : BoxedInTransferNone {
1276 : : using BoxedInTransferNone::BoxedInTransferNone;
1277 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1278 : : JS::HandleValue) override;
1279 : : };
1280 : :
1281 : : struct GClosureIn : GClosureInTransferNone {
1282 : : using GClosureInTransferNone::GClosureInTransferNone;
1283 : 0 : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1284 : : GIArgument*) override {
1285 : 0 : return skip();
1286 : : }
1287 : : };
1288 : :
1289 : : struct GBytesIn : BoxedIn {
1290 : : using BoxedIn::BoxedIn;
1291 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1292 : : JS::HandleValue) override;
1293 : : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
1294 : : GIArgument* out_arg) override;
1295 : : };
1296 : :
1297 : : struct GBytesInTransferNone : GBytesIn {
1298 : : using GBytesIn::GBytesIn;
1299 : 36 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
1300 : : GIArgument* out_arg) override {
1301 : 36 : return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
1302 : : }
1303 : : };
1304 : :
1305 : : struct ObjectIn : RegisteredIn {
1306 : : using RegisteredIn::RegisteredIn;
1307 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1308 : : JS::HandleValue) override;
1309 : : // This is a smart argument, no release needed
1310 : : };
1311 : :
1312 : : struct InterfaceIn : RegisteredIn {
1313 : : using RegisteredIn::RegisteredIn;
1314 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1315 : : JS::HandleValue) override;
1316 : : // This is a smart argument, no release needed
1317 : : };
1318 : :
1319 : : struct FundamentalIn : RegisteredIn {
1320 : : using RegisteredIn::RegisteredIn;
1321 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1322 : : JS::HandleValue) override;
1323 : : // This is a smart argument, no release needed
1324 : : };
1325 : :
1326 : : struct UnionIn : RegisteredIn {
1327 : : using RegisteredIn::RegisteredIn;
1328 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1329 : : JS::HandleValue) override;
1330 : : // This is a smart argument, no release needed
1331 : : };
1332 : :
1333 : : struct NullIn : NullableIn {
1334 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1335 : : JS::HandleValue) override;
1336 : : };
1337 : :
1338 : : struct BooleanIn : SkipAll {
1339 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1340 : : JS::HandleValue) override;
1341 : : };
1342 : :
1343 : : template <typename TAG>
1344 : : struct NumericIn : SkipAll {
1345 : : static_assert(std::is_arithmetic_v<Tag::RealT<TAG>>, "Not arithmetic type");
1346 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1347 : : JS::HandleValue) override;
1348 : : };
1349 : :
1350 : : template <typename TAG>
1351 : : struct NumericInOut : NumericIn<TAG>, Positioned {
1352 : : static_assert(std::is_arithmetic_v<Tag::RealT<TAG>>, "Not arithmetic type");
1353 : 36 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1354 : : JS::HandleValue value) override {
1355 [ - + ]: 36 : if (!NumericIn<TAG>::in(cx, state, arg, value))
1356 : 0 : return false;
1357 : :
1358 : 36 : return set_inout_parameter(state, arg);
1359 : : }
1360 : 36 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1361 : : JS::MutableHandleValue value) override {
1362 : 36 : return Gjs::c_value_to_js_checked<TAG>(cx, gjs_arg_get<TAG>(arg),
1363 : 36 : value);
1364 : : }
1365 : : };
1366 : :
1367 : : using BooleanInOut = NumericInOut<Tag::GBoolean>;
1368 : :
1369 : : struct UnicharIn : SkipAll {
1370 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1371 : : JS::HandleValue) override;
1372 : : };
1373 : :
1374 : : struct GTypeIn : SkipAll {
1375 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1376 : : JS::HandleValue) override;
1377 : : };
1378 : :
1379 : : template <GITypeTag TAG = GI_TYPE_TAG_UTF8>
1380 : : struct StringInTransferNone : NullableIn {
1381 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1382 : : JS::HandleValue) override;
1383 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1384 : : GIArgument*) override;
1385 : : };
1386 : :
1387 : : struct StringIn : StringInTransferNone<GI_TYPE_TAG_UTF8> {
1388 : 1 : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1389 : : GIArgument*) override {
1390 : 1 : return skip();
1391 : : }
1392 : : };
1393 : :
1394 : : template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
1395 : : struct StringOutBase : SkipAll {
1396 : 695 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1397 : : JS::MutableHandleValue value) override {
1398 : 695 : return Gjs::c_value_to_js(cx, gjs_arg_get<char*>(arg), value);
1399 : : }
1400 : 695 : bool release(JSContext* cx, GjsFunctionCallState*, GIArgument*,
1401 : : GIArgument* out_arg [[maybe_unused]]) override {
1402 : : if constexpr (TRANSFER == GI_TRANSFER_NOTHING) {
1403 : 649 : return skip();
1404 : : } else if constexpr (TRANSFER == GI_TRANSFER_EVERYTHING) {
1405 [ + + ]: 46 : g_clear_pointer(&gjs_arg_member<char*>(out_arg), g_free);
1406 : 46 : return true;
1407 : : } else {
1408 : : return invalid(cx, G_STRFUNC);
1409 : : }
1410 : : }
1411 : : };
1412 : :
1413 : : template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
1414 : : struct StringReturn : StringOutBase<TRANSFER> {
1415 : 0 : bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
1416 : : JS::HandleValue) override {
1417 : 0 : return Argument::invalid(cx, G_STRFUNC);
1418 : : }
1419 : :
1420 : 675 : Maybe<ReturnTag> return_tag() const override {
1421 : 675 : return Some(ReturnTag{GI_TYPE_TAG_UTF8});
1422 : : }
1423 : : };
1424 : :
1425 : : template <GITransfer TRANSFER = GI_TRANSFER_NOTHING>
1426 : : struct StringOut : StringOutBase<TRANSFER>, Positioned {
1427 : 20 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
1428 : : JS::HandleValue) override {
1429 : 20 : return set_out_parameter(state, arg);
1430 : : }
1431 : : };
1432 : :
1433 : : using FilenameInTransferNone = StringInTransferNone<GI_TYPE_TAG_FILENAME>;
1434 : :
1435 : : struct FilenameIn : FilenameInTransferNone {
1436 : 0 : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1437 : : GIArgument*) override {
1438 : 0 : return skip();
1439 : : }
1440 : : };
1441 : :
1442 : : // .out is ignored for the instance parameter
1443 : : struct GTypeStructInstanceIn : Instance {
1444 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1445 : : JS::HandleValue) override;
1446 : : // no out
1447 : 0 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
1448 : : JS::MutableHandleValue) override {
1449 : 0 : return invalid(cx, G_STRFUNC);
1450 : : };
1451 : : };
1452 : :
1453 : : struct ParamInstanceIn : Instance, Transferable {
1454 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1455 : : JS::HandleValue) override;
1456 : : // no out
1457 : 0 : bool out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
1458 : : JS::MutableHandleValue) override {
1459 : 0 : return invalid(cx, G_STRFUNC);
1460 : : };
1461 : : };
1462 : :
1463 : : struct CallbackIn : SkipAll, Callback {
1464 : : using Callback::Callback;
1465 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1466 : : JS::HandleValue) override;
1467 : :
1468 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1469 : : GIArgument*) override;
1470 : : private:
1471 : : ffi_closure *m_ffi_closure;
1472 : : };
1473 : :
1474 : : struct BasicExplicitCArrayOut : ExplicitArrayBase, BasicCArray, Positioned {
1475 : 644 : explicit BasicExplicitCArrayOut(GITypeTag element_tag, unsigned length_pos,
1476 : : GITypeTag length_tag,
1477 : : GIDirection length_direction)
1478 : 644 : : ExplicitArrayBase(length_pos, length_tag, length_direction),
1479 : 644 : BasicCArray(element_tag) {}
1480 : :
1481 : 16 : bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
1482 : : JS::HandleValue) override {
1483 : 16 : return set_out_parameter(state, arg);
1484 : : };
1485 : 29 : bool out(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1486 : : JS::MutableHandleValue value) override {
1487 : 29 : GIArgument* length_arg = &(state->out_cvalue(m_length_pos));
1488 : 29 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
1489 : :
1490 : 29 : return gjs_value_from_basic_explicit_array(cx, value, m_element_tag,
1491 : 29 : arg, length);
1492 : : }
1493 : 29 : bool release(JSContext*, GjsFunctionCallState* state,
1494 : : [[maybe_unused]] GIArgument* in_arg,
1495 : : GIArgument* out_arg) override {
1496 : 29 : GIArgument* length_arg = &state->out_cvalue(m_length_pos);
1497 : 29 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
1498 : :
1499 : 29 : gjs_gi_argument_release_basic_out_array(m_transfer, m_element_tag,
1500 : : length, out_arg);
1501 : 29 : return true;
1502 : : }
1503 : :
1504 : 13 : Maybe<ReturnTag> return_tag() const override {
1505 : 13 : return Some(ReturnTag{GI_TYPE_TAG_ARRAY});
1506 : : }
1507 : : };
1508 : :
1509 : : struct BasicExplicitCArrayIn : BasicExplicitCArrayOut {
1510 : : using BasicExplicitCArrayOut::BasicExplicitCArrayOut;
1511 : :
1512 : 118 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1513 : : JS::HandleValue value) override {
1514 : : void* data;
1515 : : size_t length;
1516 : :
1517 [ + + ]: 118 : if (!gjs_array_to_basic_explicit_array(
1518 : : cx, value, m_element_tag, m_arg_name, GJS_ARGUMENT_ARGUMENT,
1519 : 118 : flags(), &data, &length))
1520 : 4 : return false;
1521 : :
1522 : 114 : gjs_gi_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
1523 : : length);
1524 : 114 : gjs_arg_set(arg, data);
1525 : 114 : return true;
1526 : : }
1527 : 93 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
1528 : : JS::MutableHandleValue) override {
1529 : 93 : return skip();
1530 : : }
1531 : 94 : bool release(JSContext*, GjsFunctionCallState* state, GIArgument* in_arg,
1532 : : [[maybe_unused]] GIArgument* out_arg) override {
1533 : 94 : GIArgument* length_arg = &state->in_cvalue(m_length_pos);
1534 : 94 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
1535 : :
1536 : : GITransfer transfer =
1537 [ + + ]: 94 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1538 : :
1539 : 94 : gjs_gi_argument_release_basic_in_array(transfer, m_element_tag, length,
1540 : : in_arg);
1541 : 94 : return true;
1542 : : }
1543 : : };
1544 : :
1545 : : struct BasicExplicitCArrayInOut : BasicExplicitCArrayIn {
1546 : : using BasicExplicitCArrayIn::BasicExplicitCArrayIn;
1547 : :
1548 : 20 : bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1549 : : JS::HandleValue value) override {
1550 [ - + ]: 20 : if (!BasicExplicitCArrayIn::in(cx, state, arg, value))
1551 : 0 : return false;
1552 : :
1553 [ + + ]: 20 : if (!gjs_arg_get<void*>(arg)) {
1554 : : // Special case where we were given JS null to also pass null for
1555 : : // length, and not a pointer to an integer that derefs to 0.
1556 : 9 : gjs_arg_unset(&state->in_cvalue(m_length_pos));
1557 : 9 : gjs_arg_unset(&state->out_cvalue(m_length_pos));
1558 : 9 : gjs_arg_unset(&state->inout_original_cvalue(m_length_pos));
1559 : :
1560 : 9 : gjs_arg_unset(&state->out_cvalue(m_arg_pos));
1561 : 9 : gjs_arg_unset(&state->inout_original_cvalue(m_arg_pos));
1562 : :
1563 : 9 : return true;
1564 : : }
1565 : :
1566 : 11 : state->out_cvalue(m_length_pos) =
1567 : 11 : state->inout_original_cvalue(m_length_pos) =
1568 : 11 : state->in_cvalue(m_length_pos);
1569 : 11 : gjs_arg_set(&state->in_cvalue(m_length_pos),
1570 : 11 : &state->out_cvalue(m_length_pos));
1571 : 11 : return set_inout_parameter(state, arg);
1572 : : }
1573 : 20 : bool out(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1574 : : JS::MutableHandleValue value) override {
1575 : 20 : GIArgument* length_arg = &(state->out_cvalue(m_length_pos));
1576 : 20 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
1577 : :
1578 : 20 : return gjs_value_from_basic_explicit_array(cx, value, m_element_tag,
1579 : 20 : arg, length);
1580 : : }
1581 : 20 : bool release(JSContext*, GjsFunctionCallState* state,
1582 : : GIArgument* in_arg [[maybe_unused]],
1583 : : GIArgument* out_arg) override {
1584 : 20 : GIArgument* length_arg = &state->out_cvalue(m_length_pos);
1585 : 20 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
1586 : :
1587 : 20 : GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
1588 [ + + ]: 40 : if (gjs_arg_get<void*>(original_out_arg) !=
1589 : 20 : gjs_arg_get<void*>(out_arg)) {
1590 : : GITransfer transfer =
1591 [ + - ]: 6 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1592 : 6 : gjs_gi_argument_release_basic_in_array(transfer, m_element_tag,
1593 : : length, original_out_arg);
1594 : : }
1595 : :
1596 : 20 : gjs_gi_argument_release_basic_out_array(m_transfer, m_element_tag,
1597 : : length, out_arg);
1598 : 20 : return true;
1599 : : }
1600 : : };
1601 : :
1602 : : struct CallerAllocatesOut : FallbackOut, CallerAllocates {
1603 : 16 : CallerAllocatesOut(const GI::TypeInfo type_info, size_t size)
1604 : 16 : : FallbackOut(type_info), CallerAllocates(size) {}
1605 : : bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
1606 : : JS::HandleValue) override;
1607 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1608 : : GIArgument*) override;
1609 : :
1610 : 0 : GjsArgumentFlags flags() const override {
1611 : 0 : return FallbackOut::flags() | GjsArgumentFlags::CALLER_ALLOCATES;
1612 : : }
1613 : : };
1614 : :
1615 : : struct BoxedCallerAllocatesOut : CallerAllocatesOut, GTypedType {
1616 : 10 : BoxedCallerAllocatesOut(const GI::TypeInfo type_info, size_t size,
1617 : : GType gtype)
1618 : 10 : : CallerAllocatesOut(type_info, size), GTypedType(gtype) {}
1619 : : bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
1620 : : GIArgument*) override;
1621 : : };
1622 : :
1623 : : struct ZeroTerminatedArrayInOut : FallbackInOut {
1624 : : using FallbackInOut::FallbackInOut;
1625 : 2 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
1626 : : GIArgument* out_arg) override {
1627 : : GITransfer transfer =
1628 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1629 : 2 : GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
1630 [ - + ]: 2 : if (!gjs_gi_argument_release_in_array(cx, transfer, m_type_info,
1631 : : original_out_arg))
1632 : 0 : return false;
1633 : :
1634 : 2 : transfer =
1635 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
1636 : 4 : return gjs_gi_argument_release_out_array(cx, transfer, m_type_info,
1637 : 2 : out_arg);
1638 : : }
1639 : : };
1640 : :
1641 : : struct ZeroTerminatedArrayIn : FallbackIn {
1642 : : using FallbackIn::FallbackIn;
1643 : 5 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
1644 : : JS::MutableHandleValue) override {
1645 : 5 : return skip();
1646 : : }
1647 : :
1648 : 5 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
1649 : : GIArgument*) override {
1650 : : GITransfer transfer =
1651 [ + - ]: 5 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1652 : :
1653 : 10 : return gjs_gi_argument_release_in_array(cx, transfer, m_type_info,
1654 : 5 : in_arg);
1655 : : }
1656 : :
1657 : 5 : GjsArgumentFlags flags() const override {
1658 : 5 : return Argument::flags() | Nullable::flags();
1659 : : }
1660 : : };
1661 : :
1662 : : struct FixedSizeArrayIn : FallbackIn {
1663 : : using FallbackIn::FallbackIn;
1664 : 2 : bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
1665 : : JS::MutableHandleValue) override {
1666 : 2 : return skip();
1667 : : }
1668 : :
1669 : 2 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
1670 : : GIArgument*) override {
1671 : : GITransfer transfer =
1672 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1673 : :
1674 : 2 : size_t size = m_type_info.array_fixed_size().value();
1675 : 4 : return gjs_gi_argument_release_in_array(cx, transfer, m_type_info, size,
1676 : 2 : in_arg);
1677 : : }
1678 : : };
1679 : :
1680 : : struct FixedSizeArrayInOut : FallbackInOut {
1681 : : using FallbackInOut::FallbackInOut;
1682 : 2 : bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
1683 : : GIArgument* out_arg) override {
1684 : : GITransfer transfer =
1685 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
1686 : 2 : GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
1687 : 2 : size_t size = m_type_info.array_fixed_size().value();
1688 [ - + ]: 2 : if (!gjs_gi_argument_release_in_array(cx, transfer, m_type_info, size,
1689 : : original_out_arg))
1690 : 0 : return false;
1691 : :
1692 : 2 : transfer =
1693 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
1694 : 4 : return gjs_gi_argument_release_out_array(cx, transfer, m_type_info,
1695 : 2 : size, out_arg);
1696 : : }
1697 : : };
1698 : :
1699 : : GJS_JSAPI_RETURN_CONVENTION
1700 : 3 : bool NotIntrospectable::in(JSContext* cx, GjsFunctionCallState* state,
1701 : : GIArgument*, JS::HandleValue) {
1702 : 3 : const char* reason_string = "invalid introspection";
1703 : :
1704 [ - + + - : 3 : switch (m_reason) {
+ - - - -
- ]
1705 : 0 : case CALLBACK_OUT:
1706 : 0 : reason_string = "callback out-argument";
1707 : 0 : break;
1708 : 1 : case DESTROY_NOTIFY_NO_CALLBACK:
1709 : 1 : reason_string = "DestroyNotify argument with no callback";
1710 : 1 : break;
1711 : 1 : case DESTROY_NOTIFY_NO_USER_DATA:
1712 : 1 : reason_string = "DestroyNotify argument with no user data";
1713 : 1 : break;
1714 : 0 : case INTERFACE_TRANSFER_CONTAINER:
1715 : 0 : reason_string = "type not supported for (transfer container)";
1716 : 0 : break;
1717 : 1 : case OUT_CALLER_ALLOCATES_NON_STRUCT:
1718 : 1 : reason_string = "type not supported for (out caller-allocates)";
1719 : 1 : break;
1720 : 0 : case UNREGISTERED_BOXED_WITH_TRANSFER:
1721 : 0 : reason_string =
1722 : : "boxed type with transfer not registered as a GType";
1723 : 0 : break;
1724 : 0 : case UNREGISTERED_UNION:
1725 : 0 : reason_string = "union type not registered as a GType";
1726 : 0 : break;
1727 : 0 : case UNSUPPORTED_TYPE:
1728 : 0 : reason_string = "type not supported by introspection";
1729 : 0 : break;
1730 : 0 : case LAST_REASON:
1731 : : g_assert_not_reached();
1732 : : }
1733 : :
1734 : 3 : gjs_throw(cx,
1735 : : "Function %s() cannot be called: argument '%s' is not "
1736 : : "introspectable because it has a %s",
1737 : 6 : state->display_name().get(), m_arg_name, reason_string);
1738 : 3 : return false;
1739 : : }
1740 : :
1741 : : GJS_JSAPI_RETURN_CONVENTION
1742 : 10 : bool FallbackIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1743 : : JS::HandleValue value) {
1744 : 20 : return gjs_value_to_gi_argument(cx, value, m_type_info, m_arg_name,
1745 : 10 : GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
1746 : 10 : arg);
1747 : : }
1748 : :
1749 : : GJS_JSAPI_RETURN_CONVENTION
1750 : 11 : bool FallbackInOut::in(JSContext* cx, GjsFunctionCallState* state,
1751 : : GIArgument* arg, JS::HandleValue value) {
1752 : 11 : return gjs_value_to_gi_argument(cx, value, m_type_info, m_arg_name,
1753 : 11 : GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
1754 [ + - + - ]: 22 : arg) &&
1755 : 22 : set_inout_parameter(state, arg);
1756 : : }
1757 : :
1758 : : GJS_JSAPI_RETURN_CONVENTION
1759 : 245 : bool CArrayIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1760 : : JS::HandleValue value) {
1761 : : void* data;
1762 : : size_t length;
1763 : :
1764 [ - + ]: 245 : if (m_length_direction != GI_DIRECTION_IN) {
1765 : 0 : gjs_throw(cx,
1766 : : "Using different length argument direction for array %s is "
1767 : : "not supported for in arrays",
1768 : : m_arg_name);
1769 : 0 : return false;
1770 : : }
1771 : :
1772 [ + + ]: 245 : if (!gjs_array_to_explicit_array(cx, value, m_type_info, m_arg_name,
1773 : 245 : GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
1774 : : &data, &length))
1775 : 1 : return false;
1776 : :
1777 : 244 : gjs_gi_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
1778 : : length);
1779 : 244 : gjs_arg_set(arg, data);
1780 : 244 : return true;
1781 : : }
1782 : :
1783 : : GJS_JSAPI_RETURN_CONVENTION
1784 : 2 : bool CArrayInOut::in(JSContext* cx, GjsFunctionCallState* state,
1785 : : GIArgument* arg, JS::HandleValue value) {
1786 [ - + ]: 2 : if (m_length_direction != GI_DIRECTION_INOUT) {
1787 : 0 : gjs_throw(cx,
1788 : : "Using different length argument direction for array %s is "
1789 : : "not supported for inout arrays",
1790 : : m_arg_name);
1791 : 0 : return false;
1792 : : }
1793 : :
1794 : : void* data;
1795 : : size_t length;
1796 [ - + ]: 2 : if (!gjs_array_to_explicit_array(cx, value, m_type_info, m_arg_name,
1797 : 2 : GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
1798 : : &data, &length))
1799 : 0 : return false;
1800 : :
1801 : 2 : gjs_gi_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
1802 : : length);
1803 : 2 : gjs_arg_set(arg, data);
1804 : :
1805 : 2 : uint8_t length_pos = m_length_pos;
1806 : 2 : uint8_t ix = m_arg_pos;
1807 : :
1808 [ - + ]: 2 : if (!gjs_arg_get<void*>(arg)) {
1809 : : // Special case where we were given JS null to also pass null for
1810 : : // length, and not a pointer to an integer that derefs to 0.
1811 : 0 : gjs_arg_unset(&state->in_cvalue(length_pos));
1812 : 0 : gjs_arg_unset(&state->out_cvalue(length_pos));
1813 : 0 : gjs_arg_unset(&state->inout_original_cvalue(length_pos));
1814 : :
1815 : 0 : gjs_arg_unset(&state->out_cvalue(ix));
1816 : 0 : gjs_arg_unset(&state->inout_original_cvalue(ix));
1817 : : } else {
1818 : 2 : state->out_cvalue(length_pos) =
1819 : 2 : state->inout_original_cvalue(length_pos) =
1820 : 2 : state->in_cvalue(length_pos);
1821 : 2 : gjs_arg_set(&state->in_cvalue(length_pos),
1822 : 2 : &state->out_cvalue(length_pos));
1823 : :
1824 : 2 : state->out_cvalue(ix) = state->inout_original_cvalue(ix) = *arg;
1825 : 2 : gjs_arg_set(arg, &state->out_cvalue(ix));
1826 : : }
1827 : :
1828 : 2 : return true;
1829 : : }
1830 : :
1831 : : GJS_JSAPI_RETURN_CONVENTION
1832 : 262 : bool CallbackIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1833 : : JS::HandleValue value) {
1834 : : GjsCallbackTrampoline* trampoline;
1835 : : void* closure;
1836 : :
1837 [ + + + + : 262 : if (value.isNull() && m_nullable) {
+ + ]
1838 : 10 : closure = nullptr;
1839 : 10 : trampoline = nullptr;
1840 : 10 : m_ffi_closure = nullptr;
1841 : : } else {
1842 [ + + ]: 252 : if (JS_TypeOfValue(cx, value) != JSTYPE_FUNCTION) {
1843 : 2 : gjs_throw(cx, "Expected function for callback argument %s, got %s",
1844 : : m_arg_name, JS::InformalValueTypeName(value));
1845 : 2 : return false;
1846 : : }
1847 : :
1848 : 250 : JS::RootedObject callable(cx, &value.toObject());
1849 : 250 : bool is_object_method = !!state->instance_object;
1850 : 250 : trampoline = GjsCallbackTrampoline::create(
1851 : 250 : cx, callable, m_info, m_scope, is_object_method, false);
1852 [ - + ]: 250 : if (!trampoline)
1853 : 0 : return false;
1854 [ + + + + ]: 250 : if (m_scope == GI_SCOPE_TYPE_NOTIFIED && is_object_method) {
1855 : 2 : auto* priv = ObjectInstance::for_js(cx, state->instance_object);
1856 [ - + ]: 2 : if (!priv) {
1857 : 0 : gjs_throw(cx, "Signal connected to wrong type of object");
1858 : 0 : return false;
1859 : : }
1860 : :
1861 [ - + ]: 2 : if (!priv->associate_closure(cx, trampoline))
1862 : 0 : return false;
1863 : : }
1864 : 250 : closure = trampoline->closure();
1865 : 250 : m_ffi_closure = trampoline->get_ffi_closure();
1866 [ + - ]: 250 : }
1867 : :
1868 [ + + ]: 260 : if (has_callback_destroy()) {
1869 : 102 : GDestroyNotify destroy_notify = nullptr;
1870 [ + + ]: 102 : if (trampoline) {
1871 : : /* Adding another reference and a DestroyNotify that unsets it */
1872 : 98 : g_closure_ref(trampoline);
1873 : 98 : destroy_notify = [](void* data) {
1874 : 86 : g_assert(data);
1875 : 86 : g_closure_unref(static_cast<GClosure*>(data));
1876 : 86 : };
1877 : : }
1878 : 102 : gjs_arg_set(&state->in_cvalue(m_destroy_pos), destroy_notify);
1879 : : }
1880 [ + + ]: 260 : if (has_callback_closure())
1881 : 234 : gjs_arg_set(&state->in_cvalue(m_closure_pos), trampoline);
1882 : :
1883 [ + + + + ]: 260 : if (trampoline && m_scope == GI_SCOPE_TYPE_ASYNC) {
1884 : : // Add an extra reference that will be cleared when garbage collecting
1885 : : // async calls
1886 : 118 : g_closure_ref(trampoline);
1887 : : }
1888 : :
1889 : : bool keep_forever =
1890 [ + + ]: 418 : !has_callback_destroy() &&
1891 [ + - + + ]: 158 : (m_scope == GI_SCOPE_TYPE_FOREVER || m_scope == GI_SCOPE_TYPE_NOTIFIED);
1892 : :
1893 [ + + + + ]: 260 : if (trampoline && keep_forever) {
1894 : 1 : trampoline->mark_forever();
1895 : : }
1896 : 260 : gjs_arg_set(arg, closure);
1897 : :
1898 : 260 : return true;
1899 : : }
1900 : :
1901 : : GJS_JSAPI_RETURN_CONVENTION
1902 : 120 : bool FallbackOut::in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
1903 : : JS::HandleValue) {
1904 : : // Default value in case a broken C function doesn't fill in the pointer
1905 : 120 : return set_out_parameter(state, arg);
1906 : : }
1907 : :
1908 : : GJS_JSAPI_RETURN_CONVENTION
1909 : 10 : bool CallerAllocatesOut::in(JSContext*, GjsFunctionCallState* state,
1910 : : GIArgument* arg, JS::HandleValue) {
1911 : 10 : void* blob = g_malloc0(m_allocates_size);
1912 : 10 : gjs_arg_set(arg, blob);
1913 : 10 : gjs_arg_set(&state->out_cvalue(m_arg_pos), blob);
1914 : 10 : return true;
1915 : : }
1916 : :
1917 : : GJS_JSAPI_RETURN_CONVENTION
1918 : 18 : bool NullIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1919 : : JS::HandleValue) {
1920 : 18 : return handle_nullable(cx, arg, m_arg_name);
1921 : : }
1922 : :
1923 : : GJS_JSAPI_RETURN_CONVENTION
1924 : 238 : bool BooleanIn::in(JSContext*, GjsFunctionCallState*, GIArgument* arg,
1925 : : JS::HandleValue value) {
1926 : 238 : gjs_arg_set(arg, JS::ToBoolean(value));
1927 : 238 : return true;
1928 : : }
1929 : :
1930 : : template <typename TAG>
1931 : 29536 : GJS_JSAPI_RETURN_CONVENTION bool NumericIn<TAG>::in(JSContext* cx,
1932 : : GjsFunctionCallState*,
1933 : : GIArgument* arg,
1934 : : JS::HandleValue value) {
1935 : 29536 : bool out_of_range = false;
1936 : :
1937 [ + + ]: 29536 : if (!gjs_arg_set_from_js_value<TAG>(cx, value, arg, &out_of_range)) {
1938 [ + + ]: 33 : if (out_of_range) {
1939 : 8 : gjs_throw(cx, "Argument %s: value is out of range for %s",
1940 : : arg_name(), Gjs::static_type_name<TAG>());
1941 : : }
1942 : :
1943 : 33 : return false;
1944 : : }
1945 : :
1946 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
1947 : : Gjs::AutoChar{gjs_argument_display_name(
1948 : : arg_name(), GJS_ARGUMENT_ARGUMENT)}
1949 : : .get(),
1950 : : std::to_string(gjs_arg_get<TAG>(arg)).c_str(),
1951 : : Gjs::static_type_name<TAG>());
1952 : :
1953 : 29503 : return true;
1954 : : }
1955 : :
1956 : : GJS_JSAPI_RETURN_CONVENTION
1957 : 3 : bool UnicharIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1958 : : JS::HandleValue value) {
1959 [ - + ]: 3 : if (!value.isString())
1960 : 0 : return report_typeof_mismatch(cx, m_arg_name, value,
1961 : 0 : ExpectedType::STRING);
1962 : :
1963 : 3 : return gjs_unichar_from_string(cx, value, &gjs_arg_member<char32_t>(arg));
1964 : : }
1965 : :
1966 : : GJS_JSAPI_RETURN_CONVENTION
1967 : 750 : bool GTypeIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
1968 : : JS::HandleValue value) {
1969 [ - + ]: 750 : if (value.isNull())
1970 : 0 : return report_invalid_null(cx, m_arg_name);
1971 [ - + ]: 750 : if (!value.isObject())
1972 : 0 : return report_typeof_mismatch(cx, m_arg_name, value,
1973 : 0 : ExpectedType::OBJECT);
1974 : :
1975 : 750 : JS::RootedObject gtype_obj(cx, &value.toObject());
1976 : 750 : return gjs_gtype_get_actual_gtype(cx, gtype_obj,
1977 : 1500 : &gjs_arg_member<Tag::GType>(arg));
1978 : 750 : }
1979 : :
1980 : : // Common code for most types that are pointers on the C side
1981 : 13954 : bool Nullable::handle_nullable(JSContext* cx, GIArgument* arg,
1982 : : const char* arg_name) {
1983 [ + + ]: 13954 : if (!m_nullable)
1984 : 3 : return report_invalid_null(cx, arg_name);
1985 : 13951 : gjs_arg_unset(arg);
1986 : 13951 : return true;
1987 : : }
1988 : :
1989 : : template <GITypeTag TAG>
1990 : 5042 : GJS_JSAPI_RETURN_CONVENTION bool StringInTransferNone<TAG>::in(
1991 : : JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
1992 : : JS::HandleValue value) {
1993 [ + + ]: 5042 : if (value.isNull())
1994 : 33 : return NullableIn::in(cx, state, arg, value);
1995 : :
1996 [ + + ]: 5009 : if (!value.isString())
1997 : 2 : return report_typeof_mismatch(cx, m_arg_name, value,
1998 : 2 : ExpectedType::STRING);
1999 : :
2000 : : if constexpr (TAG == GI_TYPE_TAG_FILENAME) {
2001 : 175 : AutoChar str;
2002 [ - + ]: 175 : if (!gjs_string_to_filename(cx, value, &str))
2003 : 0 : return false;
2004 : 175 : gjs_arg_set(arg, str.release());
2005 : 175 : return true;
2006 : 175 : } else if constexpr (TAG == GI_TYPE_TAG_UTF8) {
2007 : 4832 : JS::UniqueChars str = gjs_string_to_utf8(cx, value);
2008 [ - + ]: 4832 : if (!str)
2009 : 0 : return false;
2010 : 4832 : gjs_arg_set(arg, js_chars_to_glib(std::move(str)).release());
2011 : 4832 : return true;
2012 : 4832 : } else {
2013 : : return invalid(cx, G_STRFUNC);
2014 : : }
2015 : : }
2016 : :
2017 : : GJS_JSAPI_RETURN_CONVENTION
2018 : 33 : bool EnumIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
2019 : : JS::HandleValue value) {
2020 : : int64_t number;
2021 [ - + ]: 33 : if (!Gjs::js_value_to_c<int64_t>(cx, value, &number))
2022 : 0 : return false;
2023 : :
2024 : : // Unpack the values from their uint32_t bitfield. See note in
2025 : : // Enum::Enum().
2026 : : int64_t min, max;
2027 [ + + ]: 33 : if (m_unsigned) {
2028 : 2 : min = m_min;
2029 : 2 : max = m_max;
2030 : : } else {
2031 : 31 : min = static_cast<int32_t>(m_min);
2032 : 31 : max = static_cast<int32_t>(m_max);
2033 : : }
2034 : :
2035 [ + - - + ]: 33 : if (number > max || number < min) {
2036 : 0 : gjs_throw(cx, "%" PRId64 " is not a valid value for enum argument %s",
2037 : : number, m_arg_name);
2038 : 0 : return false;
2039 : : }
2040 : :
2041 [ + + ]: 33 : if (m_unsigned)
2042 : 2 : gjs_arg_set<Tag::UnsignedEnum>(arg, number);
2043 : : else
2044 : 31 : gjs_arg_set<Tag::Enum>(arg, number);
2045 : :
2046 : 33 : return true;
2047 : : }
2048 : :
2049 : : GJS_JSAPI_RETURN_CONVENTION
2050 : 519 : bool FlagsIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
2051 : : JS::HandleValue value) {
2052 : : int64_t number;
2053 [ - + ]: 519 : if (!Gjs::js_value_to_c<int64_t>(cx, value, &number))
2054 : 0 : return false;
2055 : :
2056 [ - + ]: 519 : if ((uint64_t(number) & m_mask) != uint64_t(number)) {
2057 : 0 : gjs_throw(cx,
2058 : : "0x%" PRId64 " is not a valid value for flags argument %s",
2059 : : number, m_arg_name);
2060 : 0 : return false;
2061 : : }
2062 : :
2063 : : // We cast to unsigned because that's what makes sense, but then we
2064 : : // put it in the v_int slot because that's what we use to unmarshal
2065 : : // flags types at the moment.
2066 : 519 : gjs_arg_set<Tag::Enum>(arg, static_cast<unsigned>(number));
2067 : 519 : return true;
2068 : : }
2069 : :
2070 : : GJS_JSAPI_RETURN_CONVENTION
2071 : 9 : bool ForeignStructInstanceIn::in(JSContext* cx, GjsFunctionCallState*,
2072 : : GIArgument* arg, JS::HandleValue value) {
2073 : 18 : return gjs_struct_foreign_convert_to_gi_argument(
2074 : 9 : cx, value, m_info, m_arg_name, GJS_ARGUMENT_ARGUMENT, m_transfer,
2075 : 18 : flags(), arg);
2076 : : }
2077 : :
2078 : : GJS_JSAPI_RETURN_CONVENTION
2079 : 208 : bool GValueIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
2080 : : JS::HandleValue value) {
2081 [ + + ]: 208 : if (value.isObject()) {
2082 : 179 : JS::RootedObject obj(cx, &value.toObject());
2083 : : GType gtype;
2084 : :
2085 [ - + ]: 179 : if (!gjs_gtype_get_actual_gtype(cx, obj, >ype))
2086 : 0 : return false;
2087 : :
2088 [ + + ]: 179 : if (gtype == G_TYPE_VALUE) {
2089 : 148 : gjs_arg_set(arg, BoxedBase::to_c_ptr<GValue>(cx, obj));
2090 : 148 : state->ignore_release.insert(arg);
2091 : 148 : return true;
2092 : : }
2093 [ + + ]: 179 : }
2094 : :
2095 : 60 : Gjs::AutoGValue gvalue;
2096 : :
2097 [ - + ]: 60 : if (!gjs_value_to_g_value(cx, value, &gvalue))
2098 : 0 : return false;
2099 : :
2100 : 60 : gjs_arg_set(arg, g_boxed_copy(G_TYPE_VALUE, &gvalue));
2101 : :
2102 : 60 : return true;
2103 : 60 : }
2104 : :
2105 : : GJS_JSAPI_RETURN_CONVENTION
2106 : 67211 : bool BoxedInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
2107 : : GIArgument* arg, JS::HandleValue value) {
2108 [ + + ]: 67211 : if (value.isNull())
2109 : 13614 : return NullableIn::in(cx, state, arg, value);
2110 : :
2111 : 53597 : GType gtype = RegisteredType::gtype();
2112 : :
2113 [ - + ]: 53597 : if (!value.isObject())
2114 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, gtype);
2115 : :
2116 : 53597 : JS::RootedObject object(cx, &value.toObject());
2117 [ + + ]: 53597 : if (gtype == G_TYPE_ERROR) {
2118 : 12 : return ErrorBase::transfer_to_gi_argument(cx, object, arg,
2119 : 12 : GI_DIRECTION_IN, m_transfer);
2120 : : }
2121 : :
2122 [ - + - - : 53591 : if (info() && !BoxedBase::typecheck(cx, object, info().ref())) {
- + + - -
+ ]
2123 : 0 : gjs_arg_unset(arg);
2124 : 0 : return false;
2125 : : }
2126 : 107182 : return BoxedBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
2127 : 107182 : m_transfer, gtype);
2128 : 53597 : }
2129 : :
2130 : : // Unions include ClutterEvent and GdkEvent, which occur fairly often in an
2131 : : // interactive application, so they're worth a special case in a different
2132 : : // virtual function.
2133 : : GJS_JSAPI_RETURN_CONVENTION
2134 : 9 : bool UnionIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
2135 : : JS::HandleValue value) {
2136 [ - + ]: 9 : if (value.isNull())
2137 : 0 : return NullableIn::in(cx, state, arg, value);
2138 : :
2139 : 9 : GType gtype = RegisteredType::gtype();
2140 : :
2141 [ - + ]: 9 : if (!value.isObject())
2142 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, gtype);
2143 : :
2144 : 9 : JS::RootedObject object(cx, &value.toObject());
2145 : 9 : return UnionBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
2146 : 18 : m_transfer, gtype);
2147 : 9 : }
2148 : :
2149 : : GJS_JSAPI_RETURN_CONVENTION
2150 : 13489 : bool GClosureInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
2151 : : GIArgument* arg, JS::HandleValue value) {
2152 [ - + ]: 13489 : if (value.isNull())
2153 : 0 : return NullableIn::in(cx, state, arg, value);
2154 : :
2155 [ - + ]: 13489 : if (!(JS_TypeOfValue(cx, value) == JSTYPE_FUNCTION))
2156 : 0 : return report_typeof_mismatch(cx, m_arg_name, value,
2157 : 0 : ExpectedType::FUNCTION);
2158 : :
2159 : 13489 : JS::RootedObject callable(cx, &value.toObject());
2160 : 13489 : GClosure* closure = Gjs::Closure::create_marshaled(cx, callable, "boxed");
2161 : 13489 : gjs_arg_set(arg, closure);
2162 : 13489 : g_closure_ref(closure);
2163 : 13489 : g_closure_sink(closure);
2164 : :
2165 : 13489 : return true;
2166 : 13489 : }
2167 : :
2168 : : GJS_JSAPI_RETURN_CONVENTION
2169 : 71 : bool GBytesIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
2170 : : JS::HandleValue value) {
2171 [ - + ]: 71 : if (value.isNull())
2172 : 0 : return NullableIn::in(cx, state, arg, value);
2173 : :
2174 [ - + ]: 71 : if (!value.isObject())
2175 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, G_TYPE_BYTES);
2176 : :
2177 : 71 : JS::RootedObject object(cx, &value.toObject());
2178 [ + + ]: 71 : if (JS_IsUint8Array(object)) {
2179 : 21 : state->ignore_release.insert(arg);
2180 : 21 : gjs_arg_set(arg, gjs_byte_array_get_bytes(object));
2181 : 21 : return true;
2182 : : }
2183 : :
2184 : : // The bytearray path is taking an extra ref irrespective of transfer
2185 : : // ownership, so we need to do the same here.
2186 : 100 : return BoxedBase::transfer_to_gi_argument(
2187 : 50 : cx, object, arg, GI_DIRECTION_IN, GI_TRANSFER_EVERYTHING, G_TYPE_BYTES);
2188 : 71 : }
2189 : :
2190 : : GJS_JSAPI_RETURN_CONVENTION
2191 : 35 : bool GBytesIn::release(JSContext* cx, GjsFunctionCallState* state,
2192 : : GIArgument* in_arg, GIArgument* out_arg) {
2193 [ - + ]: 35 : if (state->ignore_release.erase(in_arg))
2194 : 0 : return BoxedIn::release(cx, state, in_arg, out_arg);
2195 : :
2196 : 35 : return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
2197 : : }
2198 : :
2199 : : GJS_JSAPI_RETURN_CONVENTION
2200 : 4 : bool InterfaceIn::in(JSContext* cx, GjsFunctionCallState* state,
2201 : : GIArgument* arg, JS::HandleValue value) {
2202 [ - + ]: 4 : if (value.isNull())
2203 : 0 : return NullableIn::in(cx, state, arg, value);
2204 : :
2205 : 4 : GType gtype = RegisteredType::gtype();
2206 : :
2207 [ - + ]: 4 : if (!value.isObject())
2208 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, gtype);
2209 : :
2210 : 4 : JS::RootedObject object(cx, &value.toObject());
2211 : :
2212 : : // Could be a GObject interface that's missing a prerequisite,
2213 : : // or could be a fundamental
2214 [ + - ]: 4 : if (ObjectBase::typecheck(cx, object, gtype, GjsTypecheckNoThrow{})) {
2215 : 8 : return ObjectBase::transfer_to_gi_argument(
2216 : 8 : cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
2217 : : }
2218 : :
2219 : : // If this typecheck fails, then it's neither an object nor a
2220 : : // fundamental
2221 : 0 : return FundamentalBase::transfer_to_gi_argument(
2222 : 0 : cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
2223 : 4 : }
2224 : :
2225 : : GJS_JSAPI_RETURN_CONVENTION
2226 : 2337 : bool ObjectIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
2227 : : JS::HandleValue value) {
2228 [ + + ]: 2337 : if (value.isNull())
2229 : 289 : return NullableIn::in(cx, state, arg, value);
2230 : :
2231 : 2048 : GType gtype = RegisteredType::gtype();
2232 : :
2233 [ - + ]: 2048 : if (!value.isObject())
2234 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, gtype);
2235 : :
2236 : 2048 : JS::RootedObject object(cx, &value.toObject());
2237 : 2048 : return ObjectBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
2238 : 4096 : m_transfer, gtype);
2239 : 2048 : }
2240 : :
2241 : : GJS_JSAPI_RETURN_CONVENTION
2242 : 6 : bool FundamentalIn::in(JSContext* cx, GjsFunctionCallState* state,
2243 : : GIArgument* arg, JS::HandleValue value) {
2244 [ - + ]: 6 : if (value.isNull())
2245 : 0 : return NullableIn::in(cx, state, arg, value);
2246 : :
2247 : 6 : GType gtype = RegisteredType::gtype();
2248 : :
2249 [ - + ]: 6 : if (!value.isObject())
2250 : 0 : return report_gtype_mismatch(cx, m_arg_name, value, gtype);
2251 : :
2252 : 6 : JS::RootedObject object(cx, &value.toObject());
2253 : 6 : return FundamentalBase::transfer_to_gi_argument(
2254 : 12 : cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
2255 : 6 : }
2256 : :
2257 : : GJS_JSAPI_RETURN_CONVENTION
2258 : 86 : bool GTypeStructInstanceIn::in(JSContext* cx, GjsFunctionCallState*,
2259 : : GIArgument* arg, JS::HandleValue value) {
2260 : : // Instance parameter is never nullable
2261 [ - + ]: 86 : if (!value.isObject())
2262 : 0 : return report_typeof_mismatch(cx, m_arg_name, value,
2263 : 0 : ExpectedType::OBJECT);
2264 : :
2265 : 86 : JS::RootedObject obj(cx, &value.toObject());
2266 : : GType actual_gtype;
2267 [ - + ]: 86 : if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
2268 : 0 : return false;
2269 : :
2270 [ - + ]: 86 : if (actual_gtype == G_TYPE_NONE) {
2271 : 0 : gjs_throw(cx, "Invalid GType class passed for instance parameter");
2272 : 0 : return false;
2273 : : }
2274 : :
2275 : : // We use peek here to simplify reference counting (we just ignore transfer
2276 : : // annotation, as GType classes are never really freed.) We know that the
2277 : : // GType class is referenced at least once when the JS constructor is
2278 : : // initialized.
2279 [ + - - + : 86 : if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE))
- + ]
2280 : 0 : gjs_arg_set(arg, g_type_default_interface_peek(actual_gtype));
2281 : : else
2282 : 86 : gjs_arg_set(arg, g_type_class_peek(actual_gtype));
2283 : :
2284 : 86 : return true;
2285 : 86 : }
2286 : :
2287 : : GJS_JSAPI_RETURN_CONVENTION
2288 : 161 : bool ParamInstanceIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
2289 : : JS::HandleValue value) {
2290 : : // Instance parameter is never nullable
2291 [ - + ]: 161 : if (!value.isObject())
2292 : 0 : return report_typeof_mismatch(cx, m_arg_name, value,
2293 : 0 : ExpectedType::OBJECT);
2294 : :
2295 : 161 : JS::RootedObject obj(cx, &value.toObject());
2296 [ - + ]: 161 : if (!gjs_typecheck_param(cx, obj, G_TYPE_PARAM, true))
2297 : 0 : return false;
2298 : 161 : gjs_arg_set(arg, gjs_g_param_from_param(cx, obj));
2299 : :
2300 [ - + ]: 161 : if (m_transfer == GI_TRANSFER_EVERYTHING)
2301 : 0 : g_param_spec_ref(gjs_arg_get<GParamSpec*>(arg));
2302 : :
2303 : 161 : return true;
2304 : 161 : }
2305 : :
2306 : : GJS_JSAPI_RETURN_CONVENTION
2307 : 21700 : bool FallbackInOut::out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
2308 : : JS::MutableHandleValue value) {
2309 : 21700 : return gjs_value_from_gi_argument(cx, value, m_type_info, arg, true);
2310 : : }
2311 : :
2312 : : GJS_JSAPI_RETURN_CONVENTION
2313 : 12 : bool CArrayInOut::out(JSContext* cx, GjsFunctionCallState* state,
2314 : : GIArgument* arg, JS::MutableHandleValue value) {
2315 : : GIArgument* length_arg;
2316 : :
2317 [ + - ]: 12 : if (m_length_direction != GI_DIRECTION_IN)
2318 : 12 : length_arg = &(state->out_cvalue(m_length_pos));
2319 : : else
2320 : 0 : length_arg = &(state->in_cvalue(m_length_pos));
2321 : :
2322 : 12 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
2323 : :
2324 : 24 : return gjs_value_from_explicit_array(cx, value, m_type_info, m_transfer,
2325 : 12 : arg, length);
2326 : : }
2327 : :
2328 : : GJS_JSAPI_RETURN_CONVENTION
2329 : 3 : bool FallbackIn::release(JSContext* cx, GjsFunctionCallState* state,
2330 : : GIArgument* in_arg, GIArgument*) {
2331 : : GITransfer transfer =
2332 [ + - ]: 3 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
2333 : 3 : return gjs_gi_argument_release_in_arg(cx, transfer, m_type_info, in_arg);
2334 : : }
2335 : :
2336 : : GJS_JSAPI_RETURN_CONVENTION
2337 : 21679 : bool FallbackOut::release(JSContext* cx, GjsFunctionCallState*,
2338 : : GIArgument* in_arg [[maybe_unused]],
2339 : : GIArgument* out_arg) {
2340 : 21679 : return gjs_gi_argument_release(cx, m_transfer, m_type_info, out_arg);
2341 : : }
2342 : :
2343 : : GJS_JSAPI_RETURN_CONVENTION
2344 : 7 : bool FallbackInOut::release(JSContext* cx, GjsFunctionCallState* state,
2345 : : GIArgument*, GIArgument* out_arg) {
2346 : : GITransfer transfer =
2347 [ + - ]: 7 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
2348 : 7 : GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
2349 : :
2350 : : // Assume that inout transfer means that in and out transfer are the same.
2351 : : // See https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/192
2352 : :
2353 [ + + ]: 13 : if (gjs_arg_get<void*>(original_out_arg) != gjs_arg_get<void*>(out_arg) &&
2354 [ - + - + ]: 13 : !gjs_gi_argument_release_in_arg(cx, transfer, m_type_info,
2355 : : original_out_arg))
2356 : 0 : return false;
2357 : :
2358 : 7 : return gjs_gi_argument_release(cx, transfer, m_type_info, out_arg);
2359 : : }
2360 : :
2361 : : GJS_JSAPI_RETURN_CONVENTION
2362 : 10 : bool CArrayOut::release(JSContext* cx, GjsFunctionCallState* state,
2363 : : GIArgument* in_arg [[maybe_unused]],
2364 : : GIArgument* out_arg) {
2365 : 10 : GIArgument* length_arg = &state->out_cvalue(m_length_pos);
2366 : 10 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
2367 : :
2368 : 20 : return gjs_gi_argument_release_out_array(cx, m_transfer, m_type_info,
2369 : 10 : length, out_arg);
2370 : : }
2371 : :
2372 : : GJS_JSAPI_RETURN_CONVENTION
2373 : 244 : bool CArrayIn::release(JSContext* cx, GjsFunctionCallState* state,
2374 : : GIArgument* in_arg,
2375 : : GIArgument* out_arg [[maybe_unused]]) {
2376 : 244 : GIArgument* length_arg = &state->in_cvalue(m_length_pos);
2377 : 244 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
2378 : :
2379 : : GITransfer transfer =
2380 [ + - ]: 244 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
2381 : :
2382 : 488 : return gjs_gi_argument_release_in_array(cx, transfer, m_type_info, length,
2383 : 244 : in_arg);
2384 : : }
2385 : :
2386 : : GJS_JSAPI_RETURN_CONVENTION
2387 : 2 : bool CArrayInOut::release(JSContext* cx, GjsFunctionCallState* state,
2388 : : GIArgument* in_arg [[maybe_unused]],
2389 : : GIArgument* out_arg) {
2390 : 2 : GIArgument* length_arg = &state->out_cvalue(m_length_pos);
2391 : 2 : size_t length = gjs_gi_argument_get_array_length(m_tag, length_arg);
2392 : : GITransfer transfer =
2393 [ + - ]: 2 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
2394 : :
2395 : 2 : GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
2396 : : // Due to https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/192
2397 : : // Here we've to guess what to do, but in general is "better" to leak than
2398 : : // crash, so let's assume that in/out transfer is matching.
2399 [ + - ]: 2 : if (gjs_arg_get<void*>(original_out_arg) != gjs_arg_get<void*>(out_arg)) {
2400 [ - + ]: 2 : if (!gjs_gi_argument_release_in_array(cx, transfer, m_type_info, length,
2401 : : original_out_arg))
2402 : 0 : return false;
2403 : : }
2404 : :
2405 : 4 : return gjs_gi_argument_release_out_array(cx, transfer, m_type_info, length,
2406 : 2 : out_arg);
2407 : : }
2408 : :
2409 : : GJS_JSAPI_RETURN_CONVENTION
2410 : 6 : bool CallerAllocatesOut::release(JSContext*, GjsFunctionCallState*,
2411 : : GIArgument* in_arg,
2412 : : GIArgument* out_arg [[maybe_unused]]) {
2413 : 6 : g_free(gjs_arg_steal<void*>(in_arg));
2414 : 6 : return true;
2415 : : }
2416 : :
2417 : : GJS_JSAPI_RETURN_CONVENTION
2418 : 4 : bool BoxedCallerAllocatesOut::release(JSContext*, GjsFunctionCallState*,
2419 : : GIArgument* in_arg, GIArgument*) {
2420 : 4 : g_boxed_free(m_gtype, gjs_arg_steal<void*>(in_arg));
2421 : 4 : return true;
2422 : : }
2423 : :
2424 : : GJS_JSAPI_RETURN_CONVENTION
2425 : 260 : bool CallbackIn::release(JSContext*, GjsFunctionCallState*, GIArgument* in_arg,
2426 : : GIArgument* out_arg [[maybe_unused]]) {
2427 : 260 : ffi_closure *closure = m_ffi_closure;
2428 [ + + ]: 260 : if (!closure)
2429 : 10 : return true;
2430 : :
2431 : 250 : g_closure_unref(static_cast<GClosure*>(closure->user_data));
2432 : : // CallbackTrampolines are refcounted because for notified/async closures
2433 : : // it is possible to destroy it while in call, and therefore we cannot
2434 : : // check its scope at this point
2435 : 250 : gjs_arg_unset(in_arg);
2436 : 250 : return true;
2437 : : }
2438 : :
2439 : : template <GITypeTag TAG>
2440 : 5037 : GJS_JSAPI_RETURN_CONVENTION bool StringInTransferNone<TAG>::release(
2441 : : JSContext*, GjsFunctionCallState*, GIArgument* in_arg,
2442 : : GIArgument* out_arg [[maybe_unused]]) {
2443 : 5037 : g_free(gjs_arg_get<void*>(in_arg));
2444 : 5037 : return true;
2445 : : }
2446 : :
2447 : : GJS_JSAPI_RETURN_CONVENTION
2448 : 9 : bool ForeignStructIn::release(JSContext* cx, GjsFunctionCallState* state,
2449 : : GIArgument* in_arg,
2450 : : GIArgument* out_arg [[maybe_unused]]) {
2451 : : GITransfer transfer =
2452 [ + - ]: 9 : state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
2453 : :
2454 [ + + ]: 9 : if (transfer == GI_TRANSFER_NOTHING)
2455 : 8 : return gjs_struct_foreign_release_gi_argument(cx, m_transfer, m_info,
2456 : 4 : in_arg);
2457 : :
2458 : 5 : return true;
2459 : : }
2460 : :
2461 : : GJS_JSAPI_RETURN_CONVENTION
2462 : 13620 : bool BoxedInTransferNone::release(JSContext*, GjsFunctionCallState*,
2463 : : GIArgument* in_arg,
2464 : : GIArgument* out_arg [[maybe_unused]]) {
2465 : 13620 : GType gtype = RegisteredType::gtype();
2466 : 13620 : g_assert(g_type_is_a(gtype, G_TYPE_BOXED));
2467 : :
2468 [ - + ]: 13620 : if (!gjs_arg_get<void*>(in_arg))
2469 : 0 : return true;
2470 : :
2471 : 13620 : g_boxed_free(gtype, gjs_arg_get<void*>(in_arg));
2472 : 13620 : return true;
2473 : : }
2474 : :
2475 : : GJS_JSAPI_RETURN_CONVENTION
2476 : 208 : bool GValueInTransferNone::release(JSContext* cx, GjsFunctionCallState* state,
2477 : : GIArgument* in_arg, GIArgument* out_arg) {
2478 [ + + ]: 208 : if (state->ignore_release.erase(in_arg))
2479 : 148 : return true;
2480 : :
2481 : 60 : return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
2482 : : }
2483 : :
2484 : : } // namespace Arg
2485 : :
2486 : 0 : bool Argument::invalid(JSContext* cx, const char* func) const {
2487 [ # # ]: 0 : gjs_throw(cx, "%s not implemented", func ? func : "Function");
2488 : 0 : return false;
2489 : : }
2490 : :
2491 : 0 : bool Argument::in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
2492 : : JS::HandleValue) {
2493 : 0 : return invalid(cx, G_STRFUNC);
2494 : : }
2495 : :
2496 : 0 : bool Argument::out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
2497 : : JS::MutableHandleValue) {
2498 : 0 : return invalid(cx, G_STRFUNC);
2499 : : }
2500 : :
2501 : 0 : bool Argument::release(JSContext*, GjsFunctionCallState*, GIArgument*,
2502 : : GIArgument*) {
2503 : 0 : return true;
2504 : : }
2505 : :
2506 : : // This is a trick to print out the sizes of the structs at compile time, in
2507 : : // an error message:
2508 : : // template <int s> struct Measure;
2509 : : // Measure<sizeof(Arg::)> arg_size;
2510 : :
2511 : : #ifdef GJS_DO_ARGUMENTS_SIZE_CHECK
2512 : : template <typename T>
2513 : : constexpr size_t argument_maximum_size() {
2514 : : if constexpr (std::is_same_v<T, Arg::BasicTypeInOut> ||
2515 : : std::is_same_v<T, Arg::BasicTypeOut> ||
2516 : : std::is_same_v<T, Arg::BasicTypeReturn> ||
2517 : : std::is_same_v<T, Arg::ByteArrayIn> ||
2518 : : std::is_same_v<T, Arg::ByteArrayInOut> ||
2519 : : std::is_same_v<T, Arg::ByteArrayOut> ||
2520 : : std::is_same_v<T, Arg::ByteArrayReturn> ||
2521 : : std::is_same_v<T, Arg::ErrorIn> ||
2522 : : std::is_same_v<T, Arg::GdkAtomIn> ||
2523 : : std::is_same_v<T, Arg::NumericIn<int>> ||
2524 : : std::is_same_v<T, Arg::NumericInOut<int>> ||
2525 : : std::is_same_v<T, Arg::NumericOut<int>> ||
2526 : : std::is_same_v<T, Arg::NumericReturn<int>> ||
2527 : : std::is_same_v<T, Arg::SimpleOut> ||
2528 : : std::is_same_v<T, Arg::SkipAll>)
2529 : : return 24;
2530 : : if constexpr (std::is_same_v<T, Arg::BasicExplicitCArrayIn> ||
2531 : : std::is_same_v<T, Arg::BasicExplicitCArrayInOut> ||
2532 : : std::is_same_v<T, Arg::BasicExplicitCArrayOut> ||
2533 : : std::is_same_v<T, Arg::BasicGHashIn> ||
2534 : : std::is_same_v<T, Arg::BasicGHashInOut> ||
2535 : : std::is_same_v<T, Arg::BasicGHashOut> ||
2536 : : std::is_same_v<T, Arg::BasicGHashReturn> ||
2537 : : std::is_same_v<T, Arg::BoxedIn> ||
2538 : : std::is_same_v<T, Arg::FundamentalIn> ||
2539 : : std::is_same_v<T, Arg::GBytesIn> ||
2540 : : std::is_same_v<T, Arg::GBytesInTransferNone> ||
2541 : : std::is_same_v<T, Arg::GClosureIn> ||
2542 : : std::is_same_v<T, Arg::GClosureInTransferNone> ||
2543 : : std::is_same_v<T, Arg::GValueIn> ||
2544 : : std::is_same_v<T, Arg::GValueInTransferNone> ||
2545 : : std::is_same_v<T, Arg::InterfaceIn> ||
2546 : : std::is_same_v<T, Arg::ObjectIn> ||
2547 : : std::is_same_v<T, Arg::UnionIn>)
2548 : : return 40;
2549 : : if constexpr (std::is_same_v<T, Arg::FallbackInterfaceIn> ||
2550 : : std::is_same_v<T, Arg::ForeignStructIn> ||
2551 : : std::is_same_v<T, Arg::ForeignStructInstanceIn> ||
2552 : : std::is_same_v<T, Arg::UnregisteredBoxedIn>)
2553 : : return 48;
2554 : : if constexpr (std::is_same_v<T, Arg::CallbackIn>)
2555 : : return 56;
2556 : : if constexpr (std::is_same_v<T, Arg::CArrayIn> ||
2557 : : std::is_same_v<T, Arg::CArrayInOut> ||
2558 : : std::is_same_v<T, Arg::CArrayInOut> ||
2559 : : std::is_same_v<T, Arg::CArrayOut> ||
2560 : : std::is_same_v<T, Arg::FallbackInOut> ||
2561 : : std::is_same_v<T, Arg::FallbackOut> ||
2562 : : std::is_same_v<T, Arg::FallbackReturn> ||
2563 : : std::is_same_v<T, Arg::FixedSizeArrayInOut> ||
2564 : : std::is_same_v<T, Arg::ZeroTerminatedArrayInOut>)
2565 : : return 104;
2566 : : if constexpr (std::is_same_v<T, Arg::CallerAllocatesOut> ||
2567 : : std::is_same_v<T, Arg::FallbackIn> ||
2568 : : std::is_same_v<T, Arg::FixedSizeArrayIn> ||
2569 : : std::is_same_v<T, Arg::ZeroTerminatedArrayIn>)
2570 : : return 112;
2571 : : if constexpr (std::is_same_v<T, Arg::BoxedCallerAllocatesOut>)
2572 : : return 120;
2573 : : else
2574 : : return 32;
2575 : : }
2576 : : #endif
2577 : :
2578 : : template <typename T, Arg::Kind ArgKind>
2579 : 35991 : void Argument::init_common(const Init& init, T* arg) {
2580 : : #ifdef GJS_DO_ARGUMENTS_SIZE_CHECK
2581 : : static_assert(
2582 : : sizeof(T) <= argument_maximum_size<T>(),
2583 : : "Think very hard before increasing the size of Gjs::Arguments. "
2584 : : "One is allocated for every argument to every introspected function.");
2585 : : #endif
2586 : : if constexpr (ArgKind == Arg::Kind::INSTANCE) {
2587 : 3609 : g_assert(init.index == Argument::ABSENT &&
2588 : : "index was ignored in INSTANCE parameter");
2589 : 3609 : g_assert(init.name == nullptr &&
2590 : : "name was ignored in INSTANCE parameter");
2591 : 3609 : arg->set_instance_parameter();
2592 : : } else if constexpr (ArgKind == Arg::Kind::RETURN_VALUE) {
2593 : 9374 : g_assert(init.index == Argument::ABSENT &&
2594 : : "index was ignored in RETURN_VALUE parameter");
2595 : 9374 : g_assert(init.name == nullptr &&
2596 : : "name was ignored in RETURN_VALUE parameter");
2597 : 9374 : arg->set_return_value();
2598 : : } else {
2599 : : if constexpr (std::is_base_of_v<Arg::Positioned, T>)
2600 : 1773 : arg->set_arg_pos(init.index);
2601 : 23008 : arg->m_arg_name = init.name;
2602 : : }
2603 : :
2604 : 35991 : arg->m_skip_in = (init.flags & GjsArgumentFlags::SKIP_IN);
2605 : 35991 : arg->m_skip_out = (init.flags & GjsArgumentFlags::SKIP_OUT);
2606 : :
2607 : : if constexpr (std::is_base_of_v<Arg::Nullable, T>)
2608 : 19499 : arg->m_nullable = (init.flags & GjsArgumentFlags::MAY_BE_NULL);
2609 : :
2610 : : if constexpr (std::is_base_of_v<Arg::Transferable, T>)
2611 : 14222 : arg->m_transfer = init.transfer;
2612 : 35991 : }
2613 : :
2614 : 11935 : bool ArgsCache::initialize(JSContext* cx, const GI::CallableInfo callable) {
2615 [ - + ]: 11935 : if (m_args) {
2616 : 0 : gjs_throw(cx, "Arguments cache already initialized!");
2617 : 0 : return false;
2618 : : }
2619 : :
2620 : 11935 : GI::StackTypeInfo type_info;
2621 : 11935 : callable.load_return_type(&type_info);
2622 : :
2623 : 11935 : m_has_return =
2624 [ + + + + ]: 11935 : type_info.tag() != GI_TYPE_TAG_VOID || type_info.is_pointer();
2625 : 11935 : m_is_method = callable.is_method();
2626 : :
2627 : 11935 : int size = callable.n_args();
2628 [ + + ]: 11935 : size += (m_is_method ? 1 : 0);
2629 [ + + ]: 11935 : size += (m_has_return ? 1 : 0);
2630 : :
2631 [ - + ]: 11935 : if (size > Argument::MAX_ARGS) {
2632 : 0 : gjs_throw(cx,
2633 : : "Too many arguments, only %u are supported, while %d are "
2634 : : "provided!",
2635 : : Argument::MAX_ARGS, size);
2636 : 0 : return false;
2637 : : }
2638 : :
2639 [ + - + + ]: 47670 : m_args = new ArgumentPtr[size]{};
2640 : 11935 : return true;
2641 : : }
2642 : :
2643 : : template <Arg::Kind ArgKind, typename T>
2644 : 35991 : constexpr void ArgsCache::set_argument(T* arg, const Argument::Init& init) {
2645 : 35991 : Argument::init_common<T, ArgKind>(init, arg);
2646 : 35991 : arg_get<ArgKind>(init.index) = arg;
2647 : 35991 : }
2648 : :
2649 : : template <typename T>
2650 : 9374 : constexpr void ArgsCache::set_return(T* arg, GITransfer transfer,
2651 : : GjsArgumentFlags flags) {
2652 : 18748 : set_argument<Arg::Kind::RETURN_VALUE>(
2653 : 9374 : arg, Argument::Init{nullptr, Argument::ABSENT, transfer, flags});
2654 : 9374 : }
2655 : :
2656 : : template <typename T>
2657 : 589 : constexpr void ArgsCache::set_instance(T* arg, GITransfer transfer,
2658 : : GjsArgumentFlags flags) {
2659 : 1178 : set_argument<Arg::Kind::INSTANCE>(
2660 : 589 : arg, Argument::Init{nullptr, Argument::ABSENT, transfer, flags});
2661 : 589 : }
2662 : :
2663 : 40101 : Maybe<GType> ArgsCache::instance_type() const {
2664 : 40101 : return instance()
2665 : 40101 : .andThen(std::mem_fn(&Argument::as_instance))
2666 : 40101 : .map(std::mem_fn(&Arg::Instance::gtype));
2667 : : }
2668 : :
2669 : 73452 : Maybe<Arg::ReturnTag> ArgsCache::return_tag() const {
2670 : 73452 : return return_value().andThen(std::mem_fn(&Argument::return_tag));
2671 : : }
2672 : :
2673 : 1740 : void ArgsCache::set_skip_all(uint8_t index, const char* name) {
2674 : 1740 : set_argument(new Arg::SkipAll(),
2675 : 1740 : Argument::Init{name, index, GI_TRANSFER_NOTHING,
2676 : : GjsArgumentFlags::SKIP_ALL});
2677 : 1740 : }
2678 : :
2679 : 183 : void ArgsCache::init_out_array_length_argument(const GI::ArgInfo length_arg,
2680 : : GjsArgumentFlags flags,
2681 : : unsigned length_pos) {
2682 : : // Even if we skip the length argument most of time, we need to do some
2683 : : // basic initialization here.
2684 : 183 : g_assert(length_pos <= Argument::MAX_ARGS && "too many arguments");
2685 : 183 : uint8_t validated_length_pos = length_pos;
2686 : 183 : set_argument(
2687 : 183 : new Arg::ArrayLengthOut(),
2688 : 549 : Argument::Init{
2689 : 183 : length_arg.name(), validated_length_pos, GI_TRANSFER_NOTHING,
2690 : 183 : static_cast<GjsArgumentFlags>(flags | GjsArgumentFlags::SKIP_ALL)});
2691 : 183 : }
2692 : :
2693 : 963 : void ArgsCache::set_array_argument(const GI::CallableInfo callable,
2694 : : uint8_t gi_index,
2695 : : const GI::TypeInfo type_info,
2696 : : GIDirection direction, const GI::ArgInfo arg,
2697 : : GjsArgumentFlags flags,
2698 : : unsigned length_pos) {
2699 : 963 : g_assert(type_info.array_type() == GI_ARRAY_TYPE_C);
2700 : :
2701 : 963 : GI::AutoTypeInfo element_type{type_info.element_type()};
2702 : :
2703 : 963 : GI::StackArgInfo length_arg;
2704 : 963 : callable.load_arg(length_pos, &length_arg);
2705 : 963 : GI::StackTypeInfo length_type;
2706 : 963 : length_arg.load_type(&length_type);
2707 : 963 : GITypeTag length_tag = length_type.tag();
2708 : 963 : GIDirection length_direction = length_arg.direction();
2709 : :
2710 : 963 : Argument::Init common_args{arg.name(), gi_index, arg.ownership_transfer(),
2711 : 963 : flags};
2712 : :
2713 [ + + ]: 963 : if (direction == GI_DIRECTION_IN) {
2714 [ + + ]: 907 : if (element_type.is_basic()) {
2715 : 585 : set_argument(
2716 : : new Arg::BasicExplicitCArrayIn(element_type.tag(), length_pos,
2717 : 585 : length_tag, length_direction),
2718 : : common_args);
2719 : : } else {
2720 : 322 : set_argument(new Arg::CArrayIn(type_info, length_pos, length_tag,
2721 : 644 : length_direction),
2722 : : common_args);
2723 : : }
2724 : 907 : set_skip_all(length_pos, length_arg.name());
2725 [ + + ]: 56 : } else if (direction == GI_DIRECTION_INOUT) {
2726 [ + + ]: 25 : if (element_type.is_basic()) {
2727 : 22 : set_argument(new Arg::BasicExplicitCArrayInOut(
2728 : : element_type.tag(), length_pos, length_tag,
2729 : 22 : length_direction),
2730 : : common_args);
2731 : : } else {
2732 : 3 : set_argument(new Arg::CArrayInOut(type_info, length_pos, length_tag,
2733 : 6 : length_direction),
2734 : : common_args);
2735 : : }
2736 : 25 : set_skip_all(length_pos, length_arg.name());
2737 : : } else {
2738 [ + + ]: 31 : if (element_type.is_basic()) {
2739 : 23 : set_argument(
2740 : : new Arg::BasicExplicitCArrayOut(element_type.tag(), length_pos,
2741 : 23 : length_tag, length_direction),
2742 : : common_args);
2743 : : } else {
2744 : 8 : set_argument(new Arg::CArrayOut(type_info, length_pos, length_tag,
2745 : 16 : length_direction),
2746 : : common_args);
2747 : : }
2748 : : }
2749 : :
2750 [ + + ]: 963 : if (direction == GI_DIRECTION_OUT)
2751 : 31 : init_out_array_length_argument(length_arg, flags, length_pos);
2752 : 963 : }
2753 : :
2754 : 152 : void ArgsCache::set_array_return(const GI::CallableInfo callable,
2755 : : const GI::TypeInfo type_info,
2756 : : GjsArgumentFlags flags, unsigned length_pos) {
2757 : 152 : g_assert(type_info.array_type() == GI_ARRAY_TYPE_C);
2758 : :
2759 : 152 : GI::AutoTypeInfo element_type{type_info.element_type()};
2760 : :
2761 : 152 : GI::StackArgInfo length_arg;
2762 : 152 : callable.load_arg(length_pos, &length_arg);
2763 : 152 : GI::StackTypeInfo length_type;
2764 : 152 : length_arg.load_type(&length_type);
2765 : 152 : GITypeTag length_tag = length_type.tag();
2766 : 152 : GIDirection length_direction = length_arg.direction();
2767 : :
2768 : 152 : GITransfer transfer = callable.caller_owns();
2769 [ + + ]: 152 : if (element_type.is_basic()) {
2770 : 14 : set_return(
2771 : : new Arg::BasicExplicitCArrayOut(element_type.tag(), length_pos,
2772 : 14 : length_tag, length_direction),
2773 : : transfer, GjsArgumentFlags::NONE);
2774 : : } else {
2775 : 138 : set_return(new Arg::CArrayOut(type_info, length_pos, length_tag,
2776 : 276 : length_direction),
2777 : : transfer, GjsArgumentFlags::NONE);
2778 : : }
2779 : :
2780 : 152 : init_out_array_length_argument(length_arg, flags, length_pos);
2781 : 152 : }
2782 : :
2783 : 11935 : void ArgsCache::build_return(const GI::CallableInfo callable,
2784 : : bool* inc_counter_out) {
2785 : 11935 : g_assert(inc_counter_out && "forgot out parameter");
2786 : :
2787 [ + + ]: 11935 : if (!m_has_return) {
2788 : 2561 : *inc_counter_out = false;
2789 : 7256 : return;
2790 : : }
2791 : :
2792 : 9374 : GI::StackTypeInfo type_info;
2793 : 9374 : callable.load_return_type(&type_info);
2794 : 9374 : GITransfer transfer = callable.caller_owns();
2795 : 9374 : GITypeTag tag = type_info.tag();
2796 : :
2797 : 9374 : *inc_counter_out = true;
2798 : 9374 : GjsArgumentFlags flags = GjsArgumentFlags::SKIP_IN;
2799 : :
2800 [ + + ]: 9374 : if (callable.may_return_null())
2801 : 581 : flags |= GjsArgumentFlags::MAY_BE_NULL;
2802 : :
2803 [ + + + + : 9374 : switch (tag) {
+ + + + +
+ + + +
+ ]
2804 : 1574 : case GI_TYPE_TAG_BOOLEAN:
2805 : 1574 : set_return(new Arg::BooleanReturn(), transfer, flags);
2806 : 1574 : return;
2807 : :
2808 : 5 : case GI_TYPE_TAG_INT8:
2809 : 5 : set_return(new Arg::NumericReturn<int8_t>(), transfer, flags);
2810 : 5 : return;
2811 : :
2812 : 6 : case GI_TYPE_TAG_INT16:
2813 : 6 : set_return(new Arg::NumericReturn<int16_t>(), transfer, flags);
2814 : 6 : return;
2815 : :
2816 : 130 : case GI_TYPE_TAG_INT32:
2817 : 130 : set_return(new Arg::NumericReturn<int32_t>(), transfer, flags);
2818 : 130 : return;
2819 : :
2820 : 8 : case GI_TYPE_TAG_UINT8:
2821 : 8 : set_return(new Arg::NumericReturn<uint8_t>(), transfer, flags);
2822 : 8 : return;
2823 : :
2824 : 5 : case GI_TYPE_TAG_UINT16:
2825 : 5 : set_return(new Arg::NumericReturn<uint16_t>(), transfer, flags);
2826 : 5 : return;
2827 : :
2828 : 867 : case GI_TYPE_TAG_UINT32:
2829 : 867 : set_return(new Arg::NumericReturn<uint32_t>(), transfer, flags);
2830 : 867 : return;
2831 : :
2832 : 54 : case GI_TYPE_TAG_INT64:
2833 : 54 : set_return(new Arg::NumericReturn<int64_t>(), transfer, flags);
2834 : 54 : return;
2835 : :
2836 : 169 : case GI_TYPE_TAG_UINT64:
2837 : 169 : set_return(new Arg::NumericReturn<uint64_t>(), transfer, flags);
2838 : 169 : return;
2839 : :
2840 : 3 : case GI_TYPE_TAG_FLOAT:
2841 : 3 : set_return(new Arg::NumericReturn<float>(), transfer, flags);
2842 : 3 : return;
2843 : :
2844 : 30 : case GI_TYPE_TAG_DOUBLE:
2845 : 30 : set_return(new Arg::NumericReturn<double>(), transfer, flags);
2846 : 30 : return;
2847 : :
2848 : 1318 : case GI_TYPE_TAG_UTF8:
2849 [ + + ]: 1318 : if (transfer == GI_TRANSFER_NOTHING) {
2850 : 228 : set_return(new Arg::StringReturn<GI_TRANSFER_NOTHING>(),
2851 : : transfer, flags);
2852 : 228 : return;
2853 : : } else {
2854 : 1090 : set_return(new Arg::StringReturn<GI_TRANSFER_EVERYTHING>(),
2855 : : transfer, flags);
2856 : 1090 : return;
2857 : : }
2858 : :
2859 : 383 : case GI_TYPE_TAG_ARRAY: {
2860 : 383 : Maybe<unsigned> length_pos = type_info.array_length_index();
2861 [ + + ]: 383 : if (length_pos) {
2862 : 152 : set_array_return(callable, type_info, flags, *length_pos);
2863 : 369 : return;
2864 : : }
2865 : :
2866 : 231 : GIArrayType array_type = type_info.array_type();
2867 [ + + ]: 231 : if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
2868 : 2 : set_return(new Arg::ByteArrayReturn(), transfer, flags);
2869 : 2 : return;
2870 : : }
2871 : :
2872 [ + + ]: 229 : if (type_info.element_type().is_basic()) {
2873 [ + + ]: 215 : if (array_type == GI_ARRAY_TYPE_C) {
2874 [ + + ]: 204 : if (type_info.is_zero_terminated()) {
2875 : 197 : set_return(
2876 : 197 : new Arg::BasicCZeroTerminatedArrayReturn(type_info),
2877 : : transfer, flags);
2878 : 197 : return;
2879 : : }
2880 [ + - ]: 7 : if (type_info.array_fixed_size()) {
2881 : 7 : set_return(
2882 : 7 : new Arg::BasicCFixedSizeArrayReturn(type_info),
2883 : : transfer, flags);
2884 : 7 : return;
2885 : : }
2886 [ + + ]: 11 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
2887 : 6 : set_return(new Arg::BasicGArrayReturn(type_info), transfer,
2888 : : flags);
2889 : 6 : return;
2890 [ + - ]: 5 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
2891 : 5 : set_return(new Arg::BasicGPtrArrayReturn(type_info),
2892 : : transfer, flags);
2893 : 5 : return;
2894 : : }
2895 : : }
2896 : :
2897 : : [[fallthrough]];
2898 : : }
2899 : :
2900 : : default:
2901 : 4836 : break;
2902 : : }
2903 : :
2904 [ + + ]: 4836 : if (type_info.is_basic()) {
2905 [ + + ]: 125 : if (transfer == GI_TRANSFER_NOTHING) {
2906 : 118 : set_return(new Arg::BasicTypeReturn(tag), transfer, flags);
2907 : : } else {
2908 : 7 : set_return(new Arg::BasicTypeTransferableReturn(tag), transfer,
2909 : : flags);
2910 : : }
2911 : 125 : return;
2912 [ + + + + ]: 4711 : } else if (tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST) {
2913 [ + + ]: 37 : if (type_info.element_type().is_basic()) {
2914 : 20 : set_return(new Arg::BasicGListReturn(type_info), transfer, flags);
2915 : 20 : return;
2916 : : }
2917 [ + + ]: 4674 : } else if (tag == GI_TYPE_TAG_GHASH) {
2918 [ + - + + : 51 : if (type_info.key_type().is_basic() &&
+ + ]
2919 [ + - + - ]: 34 : type_info.value_type().is_basic()) {
2920 : 12 : set_return(new Arg::BasicGHashReturn(type_info), transfer, flags);
2921 : 12 : return;
2922 : : }
2923 : : }
2924 : :
2925 : : // in() is ignored for the return value, but skip_in is not (it is used
2926 : : // in the failure release path)
2927 : 4679 : set_return(new Arg::FallbackReturn(type_info), transfer, flags);
2928 : : }
2929 : :
2930 : : namespace Arg {
2931 : :
2932 : 252 : Enum::Enum(const GI::EnumInfo info) {
2933 : 252 : int64_t min = std::numeric_limits<int64_t>::max();
2934 : 252 : int64_t max = std::numeric_limits<int64_t>::min();
2935 [ + + ]: 3369 : for (GI::AutoValueInfo value_info : info.values()) {
2936 : 3117 : int64_t value = value_info.value();
2937 : :
2938 [ + + ]: 3117 : if (value > max)
2939 : 3080 : max = value;
2940 [ + + ]: 3117 : if (value < min)
2941 : 265 : min = value;
2942 : 3117 : }
2943 : :
2944 : : // From the docs for g_value_info_get_value(): "This will always be
2945 : : // representable as a 32-bit signed or unsigned value. The use of gint64 as
2946 : : // the return type is to allow both."
2947 : : // We stuff them both into unsigned 32-bit fields, and use a flag to tell
2948 : : // whether we have to compare them as signed.
2949 : 252 : m_min = static_cast<uint32_t>(min);
2950 : 252 : m_max = static_cast<uint32_t>(max);
2951 : :
2952 [ + + + + ]: 252 : m_unsigned = (min >= 0 && max > std::numeric_limits<int32_t>::max());
2953 : 252 : }
2954 : :
2955 : 1530 : Flags::Flags(const GI::FlagsInfo info) {
2956 : 1530 : uint64_t mask = 0;
2957 [ + + ]: 17704 : for (GI::AutoValueInfo value_info : info.values()) {
2958 : : // From the docs for g_value_info_get_value(): "This will always be
2959 : : // representable as a 32-bit signed or unsigned value. The use of
2960 : : // gint64 as the return type is to allow both."
2961 : : // We stuff both into an unsigned, int-sized field, matching the
2962 : : // internal representation of flags in GLib (which uses guint).
2963 : 16174 : mask |= static_cast<unsigned>(value_info.value());
2964 : 16174 : }
2965 : :
2966 : 1530 : m_mask = mask;
2967 : 1530 : }
2968 : :
2969 : : } // namespace Arg
2970 : :
2971 : : template <Arg::Kind ArgKind>
2972 : 9048 : void ArgsCache::build_interface_in_arg(const Argument::Init& base_args,
2973 : : const GI::BaseInfo interface_info) {
2974 : : // We do some transfer magic later, so let's ensure we don't mess up.
2975 : : // Should not happen in practice.
2976 [ - + ]: 9048 : if (G_UNLIKELY(base_args.transfer == GI_TRANSFER_CONTAINER)) {
2977 : 0 : set_argument<ArgKind>(
2978 : 0 : new Arg::NotIntrospectable(INTERFACE_TRANSFER_CONTAINER),
2979 : : base_args);
2980 : 9048 : return;
2981 : : }
2982 : :
2983 [ + + + + ]: 10578 : if (auto flags_info = interface_info.as<GI::InfoTag::FLAGS>()) {
2984 : 1530 : set_argument<ArgKind>(new Arg::FlagsIn(*flags_info), base_args);
2985 : 1530 : return;
2986 : : }
2987 : :
2988 [ + + + + ]: 7770 : if (auto enum_info = interface_info.as<GI::InfoTag::ENUM>()) {
2989 : 252 : set_argument<ArgKind>(new Arg::EnumIn(*enum_info), base_args);
2990 : 252 : return;
2991 : : }
2992 : :
2993 : 7266 : auto struct_info = interface_info.as<GI::InfoTag::STRUCT>();
2994 [ + + + + : 7266 : if (struct_info && struct_info->is_foreign()) {
+ + ]
2995 : : if constexpr (ArgKind == Arg::Kind::INSTANCE)
2996 : 0 : set_argument<ArgKind>(
2997 : 0 : new Arg::ForeignStructInstanceIn(*struct_info), base_args);
2998 : : else
2999 : 35 : set_argument<ArgKind>(new Arg::ForeignStructIn(*struct_info),
3000 : : base_args);
3001 : 35 : return;
3002 : : }
3003 : :
3004 [ + - - + ]: 14462 : if (auto reg_info = interface_info.as<GI::InfoTag::REGISTERED_TYPE>()) {
3005 [ + + ]: 7231 : if (reg_info->is_gdk_atom()) {
3006 : : if constexpr (ArgKind != Arg::Kind::INSTANCE) {
3007 : 34 : set_argument<ArgKind>(new Arg::GdkAtomIn(), base_args);
3008 : 34 : return;
3009 : : }
3010 : : }
3011 : :
3012 : 7197 : GType gtype = reg_info->gtype();
3013 : :
3014 [ + + + + : 7512 : if (struct_info && gtype == G_TYPE_NONE &&
+ + ]
3015 [ + - ]: 315 : !struct_info->is_gtype_struct()) {
3016 : : if constexpr (ArgKind != Arg::Kind::INSTANCE) {
3017 : : // This covers cases such as GTypeInstance
3018 : 307 : set_argument<ArgKind>(new Arg::FallbackInterfaceIn(*reg_info),
3019 : : base_args);
3020 : 307 : return;
3021 : : }
3022 : : }
3023 : :
3024 : : // Transfer handling is a bit complex here, because some of our in()
3025 : : // arguments know not to copy stuff if we don't need to.
3026 : :
3027 [ + + ]: 6890 : if (gtype == G_TYPE_VALUE) {
3028 : : if constexpr (ArgKind == Arg::Kind::INSTANCE)
3029 : 58 : set_argument<ArgKind>(new Arg::BoxedIn(*reg_info), base_args);
3030 [ + - ]: 27 : else if (base_args.transfer == GI_TRANSFER_NOTHING)
3031 : 27 : set_argument<ArgKind>(new Arg::GValueInTransferNone(*reg_info),
3032 : : base_args);
3033 : : else
3034 : 0 : set_argument<ArgKind>(new Arg::GValueIn(*reg_info), base_args);
3035 : 85 : return;
3036 : : }
3037 : :
3038 [ + + ]: 6805 : if (gtype == G_TYPE_CLOSURE) {
3039 [ + - ]: 725 : if (base_args.transfer == GI_TRANSFER_NOTHING &&
3040 : : ArgKind != Arg::Kind::INSTANCE)
3041 : 725 : set_argument<ArgKind>(
3042 : 1450 : new Arg::GClosureInTransferNone(*reg_info), base_args);
3043 : : else
3044 : 0 : set_argument<ArgKind>(new Arg::GClosureIn(*reg_info),
3045 : : base_args);
3046 : 725 : return;
3047 : : }
3048 : :
3049 [ + + ]: 6080 : if (gtype == G_TYPE_BYTES) {
3050 [ + - ]: 78 : if (base_args.transfer == GI_TRANSFER_NOTHING &&
3051 : : ArgKind != Arg::Kind::INSTANCE)
3052 : 78 : set_argument<ArgKind>(new Arg::GBytesInTransferNone(*reg_info),
3053 : : base_args);
3054 : : else
3055 : 2 : set_argument<ArgKind>(new Arg::GBytesIn(*reg_info), base_args);
3056 : 80 : return;
3057 : : }
3058 : :
3059 [ + + + + : 6000 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
3060 : 3917 : set_argument<ArgKind>(new Arg::ObjectIn(*reg_info), base_args);
3061 : 3917 : return;
3062 : : }
3063 : :
3064 [ + + - + : 2083 : if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
3065 : : if constexpr (ArgKind != Arg::Kind::INSTANCE) {
3066 : 147 : set_argument<ArgKind>(new Arg::FallbackInterfaceIn(*reg_info),
3067 : : base_args);
3068 : 147 : return;
3069 : : }
3070 : : }
3071 : :
3072 [ + + ]: 1936 : if (interface_info.is_union()) {
3073 [ - + ]: 15 : if (gtype == G_TYPE_NONE) {
3074 : : // Can't handle unions without a GType
3075 : 0 : set_argument<ArgKind>(
3076 : 0 : new Arg::NotIntrospectable(UNREGISTERED_UNION), base_args);
3077 : 0 : return;
3078 : : }
3079 : :
3080 : 15 : set_argument<ArgKind>(new Arg::UnionIn(*reg_info), base_args);
3081 : 15 : return;
3082 : : }
3083 : :
3084 [ + + ]: 1921 : if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
3085 : 4 : set_argument<ArgKind>(new Arg::FundamentalIn(*reg_info), base_args);
3086 : 4 : return;
3087 : : }
3088 : :
3089 [ + - + + : 1917 : if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+ + ]
3090 : 4 : set_argument<ArgKind>(new Arg::InterfaceIn(*reg_info), base_args);
3091 : 4 : return;
3092 : : }
3093 : :
3094 : : // generic boxed type
3095 [ + + ]: 1913 : if (gtype == G_TYPE_NONE) {
3096 [ - + ]: 8 : if (base_args.transfer != GI_TRANSFER_NOTHING) {
3097 : : // Can't transfer ownership of a structure type not
3098 : : // registered as a boxed
3099 : 0 : set_argument<ArgKind>(new Arg::NotIntrospectable(
3100 : : UNREGISTERED_BOXED_WITH_TRANSFER),
3101 : : base_args);
3102 : 0 : return;
3103 : : }
3104 : :
3105 : 8 : set_argument<ArgKind>(new Arg::UnregisteredBoxedIn(*struct_info),
3106 : : base_args);
3107 : 8 : return;
3108 : : }
3109 : 1905 : set_argument<ArgKind>(new Arg::BoxedIn(*reg_info), base_args);
3110 : 1905 : return;
3111 : : }
3112 : :
3113 : : // Don't know how to handle this interface type (should not happen
3114 : : // in practice, for typelibs emitted by g-ir-compiler)
3115 : 0 : set_argument<ArgKind>(new Arg::NotIntrospectable(UNSUPPORTED_TYPE),
3116 : : base_args);
3117 [ - + ]: 7266 : }
3118 : :
3119 : 18044 : void ArgsCache::build_normal_in_arg(uint8_t gi_index,
3120 : : const GI::TypeInfo type_info,
3121 : : const GI::ArgInfo arg,
3122 : : GjsArgumentFlags flags) {
3123 : : // "Normal" in arguments are those arguments that don't require special
3124 : : // processing, and don't touch other arguments.
3125 : : // Main categories are:
3126 : : // - void*
3127 : : // - small numbers (fit in 32bit)
3128 : : // - big numbers (need a double)
3129 : : // - strings
3130 : : // - enums/flags (different from numbers in the way they're exposed in GI)
3131 : : // - objects (GObjects, boxed, unions, etc.)
3132 : : // - hashes
3133 : : // - sequences (null-terminated arrays, lists, etc.)
3134 : :
3135 : 18044 : const char* name = arg.name();
3136 : 18044 : GITransfer transfer = arg.ownership_transfer();
3137 : 18044 : Argument::Init common_args{name, gi_index, transfer, flags};
3138 : :
3139 [ + + + + : 18044 : switch (type_info.tag()) {
+ + + + +
+ + + + +
+ + + + +
+ + - ]
3140 : 950 : case GI_TYPE_TAG_VOID:
3141 : 950 : set_argument(new Arg::NullIn(), common_args);
3142 : 950 : break;
3143 : :
3144 : 400 : case GI_TYPE_TAG_BOOLEAN:
3145 : 400 : set_argument(new Arg::BooleanIn(), common_args);
3146 : 400 : break;
3147 : :
3148 : 141 : case GI_TYPE_TAG_INT8:
3149 : 141 : set_argument(new Arg::NumericIn<int8_t>(), common_args);
3150 : 10111 : return;
3151 : :
3152 : 69 : case GI_TYPE_TAG_INT16:
3153 : 69 : set_argument(new Arg::NumericIn<int16_t>(), common_args);
3154 : 69 : return;
3155 : :
3156 : 1074 : case GI_TYPE_TAG_INT32:
3157 : 1074 : set_argument(new Arg::NumericIn<int32_t>(), common_args);
3158 : 1074 : return;
3159 : :
3160 : 70 : case GI_TYPE_TAG_UINT8:
3161 : 70 : set_argument(new Arg::NumericIn<uint8_t>(), common_args);
3162 : 70 : return;
3163 : :
3164 : 74 : case GI_TYPE_TAG_UINT16:
3165 : 74 : set_argument(new Arg::NumericIn<uint16_t>(), common_args);
3166 : 74 : return;
3167 : :
3168 : 1459 : case GI_TYPE_TAG_UINT32:
3169 : 1459 : set_argument(new Arg::NumericIn<uint32_t>(), common_args);
3170 : 1459 : return;
3171 : :
3172 : 259 : case GI_TYPE_TAG_INT64:
3173 : 259 : set_argument(new Arg::NumericIn<int64_t>(), common_args);
3174 : 259 : return;
3175 : :
3176 : 438 : case GI_TYPE_TAG_UINT64:
3177 : 438 : set_argument(new Arg::NumericIn<uint64_t>(), common_args);
3178 : 438 : return;
3179 : :
3180 : 3 : case GI_TYPE_TAG_FLOAT:
3181 : 3 : set_argument(new Arg::NumericIn<float>(), common_args);
3182 : 3 : return;
3183 : :
3184 : 235 : case GI_TYPE_TAG_DOUBLE:
3185 : 235 : set_argument(new Arg::NumericIn<double>(), common_args);
3186 : 235 : return;
3187 : :
3188 : 1 : case GI_TYPE_TAG_UNICHAR:
3189 : 1 : set_argument(new Arg::UnicharIn(), common_args);
3190 : 1 : break;
3191 : :
3192 : 418 : case GI_TYPE_TAG_GTYPE:
3193 : 418 : set_argument(new Arg::GTypeIn(), common_args);
3194 : 418 : break;
3195 : :
3196 : 230 : case GI_TYPE_TAG_FILENAME:
3197 [ + - ]: 230 : if (transfer == GI_TRANSFER_NOTHING)
3198 : 230 : set_argument(new Arg::FilenameInTransferNone(), common_args);
3199 : : else
3200 : 0 : set_argument(new Arg::FilenameIn(), common_args);
3201 : 230 : break;
3202 : :
3203 : 5932 : case GI_TYPE_TAG_UTF8:
3204 [ + + ]: 5932 : if (transfer == GI_TRANSFER_NOTHING)
3205 : 5931 : set_argument(new Arg::StringInTransferNone<GI_TYPE_TAG_UTF8>(),
3206 : : common_args);
3207 : : else
3208 : 1 : set_argument(new Arg::StringIn(), common_args);
3209 : 5932 : break;
3210 : :
3211 : 6028 : case GI_TYPE_TAG_INTERFACE: {
3212 : 6028 : build_interface_in_arg(common_args, type_info.interface());
3213 : 6028 : return;
3214 : : }
3215 : :
3216 : 74 : case GI_TYPE_TAG_ERROR:
3217 : 74 : set_argument(new Arg::ErrorIn(), common_args);
3218 : 74 : return;
3219 : :
3220 : 24 : case GI_TYPE_TAG_GLIST:
3221 : : case GI_TYPE_TAG_GSLIST:
3222 [ + + ]: 24 : if (type_info.element_type().is_basic()) {
3223 : 14 : set_argument(new Arg::BasicGListIn(type_info), common_args);
3224 : 14 : return;
3225 : : }
3226 : :
3227 : : // Fall back to the generic marshaller
3228 : 10 : set_argument(new Arg::FallbackIn(type_info), common_args);
3229 : 10 : return;
3230 : :
3231 : 12 : case GI_TYPE_TAG_GHASH:
3232 [ + - + + : 36 : if (type_info.key_type().is_basic() &&
+ + ]
3233 [ + - + - ]: 24 : type_info.value_type().is_basic()) {
3234 : 11 : set_argument(new Arg::BasicGHashIn(type_info), common_args);
3235 : 11 : return;
3236 : : }
3237 : :
3238 : : // Fall back to the generic marshaller
3239 : 1 : set_argument(new Arg::FallbackIn(type_info), common_args);
3240 : 1 : return;
3241 : :
3242 : 153 : case GI_TYPE_TAG_ARRAY: {
3243 : 153 : GIArrayType array_type = type_info.array_type();
3244 [ + + ]: 153 : if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
3245 : 2 : set_argument(new Arg::ByteArrayIn(), common_args);
3246 : 2 : return;
3247 : : }
3248 : :
3249 [ + + ]: 151 : if (type_info.element_type().is_basic()) {
3250 [ + + ]: 142 : if (array_type == GI_ARRAY_TYPE_ARRAY) {
3251 : 6 : set_argument(new Arg::BasicGArrayIn(type_info),
3252 : : common_args);
3253 : 6 : return;
3254 : : }
3255 : :
3256 [ + + ]: 136 : if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
3257 : 2 : set_argument(new Arg::BasicGPtrArrayIn(type_info),
3258 : : common_args);
3259 : 2 : return;
3260 : : }
3261 : :
3262 [ + - ]: 134 : if (array_type == GI_ARRAY_TYPE_C) {
3263 [ + + ]: 134 : if (type_info.is_zero_terminated()) {
3264 : 124 : set_argument(
3265 : 124 : new Arg::BasicCZeroTerminatedArrayIn(type_info),
3266 : : common_args);
3267 : 124 : return;
3268 : : }
3269 [ + + ]: 10 : if (type_info.array_fixed_size()) {
3270 : 8 : set_argument(new Arg::BasicCFixedSizeArrayIn(type_info),
3271 : : common_args);
3272 : 8 : return;
3273 : : }
3274 : : }
3275 : : }
3276 : :
3277 [ + - ]: 11 : if (array_type == GI_ARRAY_TYPE_C) {
3278 [ + + ]: 11 : if (type_info.is_zero_terminated()) {
3279 : 6 : set_argument(new Arg::ZeroTerminatedArrayIn(type_info),
3280 : : common_args);
3281 : 6 : return;
3282 : : }
3283 [ + + ]: 5 : if (type_info.array_fixed_size()) {
3284 : 3 : set_argument(new Arg::FixedSizeArrayIn(type_info),
3285 : : common_args);
3286 : 3 : return;
3287 : : }
3288 : : }
3289 : : [[fallthrough]];
3290 : : }
3291 : :
3292 : : default:
3293 : : // FIXME: Falling back to the generic marshaller
3294 : 2 : set_argument(new Arg::FallbackIn(type_info), common_args);
3295 : : }
3296 : : }
3297 : :
3298 : 769 : void ArgsCache::build_normal_out_arg(uint8_t gi_index,
3299 : : const GI::TypeInfo type_info,
3300 : : const GI::ArgInfo arg,
3301 : : GjsArgumentFlags flags) {
3302 : 769 : GITransfer transfer = arg.ownership_transfer();
3303 : 769 : Argument::Init common_args{arg.name(), gi_index, transfer, flags};
3304 : 769 : GITypeTag tag = type_info.tag();
3305 : :
3306 [ + + + + : 769 : switch (tag) {
+ + + + +
+ + + + ]
3307 : 73 : case GI_TYPE_TAG_BOOLEAN:
3308 : 73 : set_argument(new Arg::BooleanOut(), common_args);
3309 : 73 : break;
3310 : :
3311 : 7 : case GI_TYPE_TAG_INT8:
3312 : 7 : set_argument(new Arg::NumericOut<int8_t>(), common_args);
3313 : 401 : return;
3314 : :
3315 : 6 : case GI_TYPE_TAG_INT16:
3316 : 6 : set_argument(new Arg::NumericOut<int16_t>(), common_args);
3317 : 6 : return;
3318 : :
3319 : 73 : case GI_TYPE_TAG_INT32:
3320 : 73 : set_argument(new Arg::NumericOut<int32_t>(), common_args);
3321 : 73 : return;
3322 : :
3323 : 2 : case GI_TYPE_TAG_UINT8:
3324 : 2 : set_argument(new Arg::NumericOut<uint8_t>(), common_args);
3325 : 2 : return;
3326 : :
3327 : 4 : case GI_TYPE_TAG_UINT16:
3328 : 4 : set_argument(new Arg::NumericOut<uint16_t>(), common_args);
3329 : 4 : return;
3330 : :
3331 : 22 : case GI_TYPE_TAG_UINT32:
3332 : 22 : set_argument(new Arg::NumericOut<uint32_t>(), common_args);
3333 : 22 : return;
3334 : :
3335 : 19 : case GI_TYPE_TAG_INT64:
3336 : 19 : set_argument(new Arg::NumericOut<int64_t>(), common_args);
3337 : 19 : return;
3338 : :
3339 : 21 : case GI_TYPE_TAG_UINT64:
3340 : 21 : set_argument(new Arg::NumericOut<uint64_t>(), common_args);
3341 : 21 : return;
3342 : :
3343 : 9 : case GI_TYPE_TAG_FLOAT:
3344 : 9 : set_argument(new Arg::NumericOut<float>(), common_args);
3345 : 9 : return;
3346 : :
3347 : 18 : case GI_TYPE_TAG_DOUBLE:
3348 : 18 : set_argument(new Arg::NumericOut<double>(), common_args);
3349 : 18 : return;
3350 : :
3351 : 90 : case GI_TYPE_TAG_UTF8:
3352 [ + + ]: 90 : if (transfer == GI_TRANSFER_NOTHING) {
3353 : 6 : set_argument(new Arg::StringOut<GI_TRANSFER_NOTHING>(),
3354 : : common_args);
3355 : : } else {
3356 : 84 : set_argument(new Arg::StringOut<GI_TRANSFER_EVERYTHING>(),
3357 : : common_args);
3358 : : }
3359 : 90 : return;
3360 : :
3361 : 498 : default: {
3362 : : }
3363 : : }
3364 : :
3365 [ + + ]: 498 : if (type_info.is_basic()) {
3366 [ + + ]: 77 : if (transfer == GI_TRANSFER_NOTHING) {
3367 : 1 : set_argument(new Arg::BasicTypeOut(tag), common_args);
3368 : : } else {
3369 : 76 : set_argument(new Arg::BasicTypeTransferableOut(tag), common_args);
3370 : : }
3371 : 77 : return;
3372 [ + + ]: 421 : } else if (tag == GI_TYPE_TAG_ARRAY) {
3373 : 42 : GIArrayType array_type = type_info.array_type();
3374 [ + + ]: 42 : if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
3375 : 1 : set_argument(new Arg::ByteArrayOut(), common_args);
3376 : 1 : return;
3377 : : }
3378 : :
3379 [ + + ]: 41 : if (type_info.element_type().is_basic()) {
3380 [ + + ]: 31 : if (array_type == GI_ARRAY_TYPE_C) {
3381 [ + + ]: 19 : if (type_info.is_zero_terminated()) {
3382 : 12 : set_argument(
3383 : 12 : new Arg::BasicCZeroTerminatedArrayOut(type_info),
3384 : : common_args);
3385 : 12 : return;
3386 : : }
3387 : :
3388 [ + - ]: 7 : if (type_info.array_fixed_size()) {
3389 : 7 : set_argument(new Arg::BasicCFixedSizeArrayOut(type_info),
3390 : : common_args);
3391 : 7 : return;
3392 : : }
3393 [ + + ]: 12 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
3394 : 6 : set_argument(new Arg::BasicGArrayOut(type_info), common_args);
3395 : 6 : return;
3396 [ + - ]: 6 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
3397 : 6 : set_argument(new Arg::BasicGPtrArrayOut(type_info),
3398 : : common_args);
3399 : 6 : return;
3400 : : }
3401 : : }
3402 : : }
3403 : :
3404 [ + + + + ]: 389 : if (tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST) {
3405 [ + - ]: 14 : if (type_info.element_type().is_basic()) {
3406 : 14 : set_argument(new Arg::BasicGListOut(type_info), common_args);
3407 : 14 : return;
3408 : : }
3409 [ + + ]: 375 : } else if (tag == GI_TYPE_TAG_GHASH) {
3410 [ + - + - : 21 : if (type_info.key_type().is_basic() &&
+ - ]
3411 [ + - + - ]: 14 : type_info.value_type().is_basic()) {
3412 : 7 : set_argument(new Arg::BasicGHashOut(type_info), common_args);
3413 : 7 : return;
3414 : : }
3415 [ + + ]: 368 : } else if (tag == GI_TYPE_TAG_ARRAY) {
3416 : 10 : GIArrayType array_type = type_info.array_type();
3417 [ - + ]: 10 : if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
3418 : 0 : set_argument(new Arg::ByteArrayOut(), common_args);
3419 : 0 : return;
3420 : : }
3421 : :
3422 [ - + ]: 10 : if (type_info.element_type().is_basic()) {
3423 [ # # ]: 0 : if (array_type == GI_ARRAY_TYPE_C) {
3424 [ # # ]: 0 : if (type_info.is_zero_terminated()) {
3425 : 0 : set_argument(
3426 : 0 : new Arg::BasicCZeroTerminatedArrayOut(type_info),
3427 : : common_args);
3428 : 0 : return;
3429 : : }
3430 : :
3431 [ # # ]: 0 : if (type_info.array_fixed_size()) {
3432 : 0 : set_argument(new Arg::BasicCFixedSizeArrayOut(type_info),
3433 : : common_args);
3434 : 0 : return;
3435 : : }
3436 [ # # ]: 0 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
3437 : 0 : set_argument(new Arg::BasicGArrayOut(type_info), common_args);
3438 : 0 : return;
3439 [ # # ]: 0 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
3440 : 0 : set_argument(new Arg::BasicGPtrArrayOut(type_info),
3441 : : common_args);
3442 : 0 : return;
3443 : : }
3444 : : }
3445 : : }
3446 : :
3447 : 368 : set_argument(new Arg::FallbackOut(type_info), common_args);
3448 : : }
3449 : :
3450 : 89 : void ArgsCache::build_normal_inout_arg(uint8_t gi_index,
3451 : : const GI::TypeInfo type_info,
3452 : : const GI::ArgInfo arg,
3453 : : GjsArgumentFlags flags) {
3454 : 89 : GITransfer transfer = arg.ownership_transfer();
3455 : 89 : Argument::Init common_args{arg.name(), gi_index, transfer, flags};
3456 : 89 : GITypeTag tag = type_info.tag();
3457 : :
3458 [ + + + + : 89 : switch (tag) {
+ + + + -
+ + + + +
+ ]
3459 : 2 : case GI_TYPE_TAG_BOOLEAN:
3460 : 2 : set_argument(new Arg::BooleanInOut(), common_args);
3461 : 2 : break;
3462 : :
3463 : 2 : case GI_TYPE_TAG_INT8:
3464 : 2 : set_argument(new Arg::NumericInOut<int8_t>(), common_args);
3465 : 78 : return;
3466 : :
3467 : 4 : case GI_TYPE_TAG_INT16:
3468 : 4 : set_argument(new Arg::NumericInOut<int16_t>(), common_args);
3469 : 4 : return;
3470 : :
3471 : 29 : case GI_TYPE_TAG_INT32:
3472 : 29 : set_argument(new Arg::NumericInOut<int32_t>(), common_args);
3473 : 29 : return;
3474 : :
3475 : 1 : case GI_TYPE_TAG_UINT8:
3476 : 1 : set_argument(new Arg::NumericInOut<uint8_t>(), common_args);
3477 : 1 : return;
3478 : :
3479 : 2 : case GI_TYPE_TAG_UINT16:
3480 : 2 : set_argument(new Arg::NumericInOut<uint16_t>(), common_args);
3481 : 2 : return;
3482 : :
3483 : 4 : case GI_TYPE_TAG_UINT32:
3484 : 4 : set_argument(new Arg::NumericInOut<uint32_t>(), common_args);
3485 : 4 : return;
3486 : :
3487 : 5 : case GI_TYPE_TAG_INT64:
3488 : 5 : set_argument(new Arg::NumericInOut<int64_t>(), common_args);
3489 : 5 : return;
3490 : :
3491 : 0 : case GI_TYPE_TAG_UINT64:
3492 : 0 : set_argument(new Arg::NumericInOut<uint64_t>(), common_args);
3493 : 0 : return;
3494 : :
3495 : 4 : case GI_TYPE_TAG_FLOAT:
3496 : 4 : set_argument(new Arg::NumericInOut<float>(), common_args);
3497 : 4 : return;
3498 : :
3499 : 1 : case GI_TYPE_TAG_DOUBLE:
3500 : 1 : set_argument(new Arg::NumericInOut<double>(), common_args);
3501 : 1 : return;
3502 : :
3503 : 2 : case GI_TYPE_TAG_GLIST:
3504 : : case GI_TYPE_TAG_GSLIST:
3505 [ + - ]: 2 : if (type_info.element_type().is_basic()) {
3506 : 2 : set_argument(new Arg::BasicGListInOut(type_info), common_args);
3507 : 2 : return;
3508 : : }
3509 : :
3510 : 0 : set_argument(new Arg::FallbackInOut(type_info), common_args);
3511 : 0 : return;
3512 : :
3513 : 1 : case GI_TYPE_TAG_GHASH:
3514 [ + - + - : 3 : if (type_info.key_type().is_basic() &&
+ - ]
3515 [ + - + - ]: 2 : type_info.value_type().is_basic()) {
3516 : 1 : set_argument(new Arg::BasicGHashInOut(type_info), common_args);
3517 : 1 : return;
3518 : : }
3519 : :
3520 : 0 : set_argument(new Arg::FallbackInOut(type_info), common_args);
3521 : 0 : return;
3522 : :
3523 : 19 : case GI_TYPE_TAG_ARRAY: {
3524 : 19 : GIArrayType array_type = type_info.array_type();
3525 : :
3526 [ + + ]: 19 : if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
3527 : 1 : set_argument(new Arg::ByteArrayInOut(), common_args);
3528 : 1 : return;
3529 : : }
3530 : :
3531 [ + + ]: 18 : if (array_type == GI_ARRAY_TYPE_C) {
3532 [ + + ]: 16 : if (type_info.is_zero_terminated()) {
3533 [ + + ]: 9 : if (type_info.element_type().is_basic()) {
3534 : 6 : set_argument(
3535 : 6 : new Arg::BasicCZeroTerminatedArrayInOut(type_info),
3536 : : common_args);
3537 : 6 : return;
3538 : : }
3539 : 3 : set_argument(new Arg::ZeroTerminatedArrayInOut(type_info),
3540 : : common_args);
3541 : 3 : return;
3542 : : }
3543 [ + - ]: 7 : if (type_info.array_fixed_size()) {
3544 [ + + ]: 7 : if (type_info.element_type().is_basic()) {
3545 : 4 : set_argument(
3546 : 4 : new Arg::BasicCFixedSizeArrayInOut(type_info),
3547 : : common_args);
3548 : 4 : return;
3549 : : }
3550 : 3 : set_argument(new Arg::FixedSizeArrayInOut(type_info),
3551 : : common_args);
3552 : 3 : return;
3553 : : }
3554 [ + + ]: 2 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
3555 : 1 : set_argument(new Arg::BasicGArrayInOut(type_info), common_args);
3556 : 1 : return;
3557 [ + - ]: 1 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
3558 : 1 : set_argument(new Arg::BasicGPtrArrayInOut(type_info),
3559 : : common_args);
3560 : 1 : return;
3561 : : }
3562 : :
3563 : 0 : set_argument(new Arg::FallbackInOut(type_info), common_args);
3564 : 0 : return;
3565 : : }
3566 : :
3567 : 15 : default: {
3568 : : }
3569 : : }
3570 : :
3571 [ + + ]: 15 : if (type_info.is_basic()) {
3572 [ + + ]: 4 : if (transfer == GI_TRANSFER_NOTHING) {
3573 : 1 : set_argument(new Arg::BasicTypeInOut(tag), common_args);
3574 : : } else {
3575 : 3 : set_argument(new Arg::BasicTypeTransferableInOut(tag), common_args);
3576 : : }
3577 : 4 : return;
3578 : : }
3579 : :
3580 : 11 : set_argument(new Arg::FallbackInOut(type_info), common_args);
3581 : : }
3582 : :
3583 : 11935 : void ArgsCache::build_instance(const GI::CallableInfo callable) {
3584 [ + + ]: 11935 : if (!m_is_method)
3585 : 8915 : return;
3586 : :
3587 : 3609 : Maybe<const GI::BaseInfo> interface_info = callable.container();
3588 : 3609 : g_assert(interface_info && "callable must be a contained type");
3589 : :
3590 : 3609 : GITransfer transfer = callable.instance_ownership_transfer();
3591 : :
3592 : : // These cases could be covered by the generic marshaller, except that
3593 : : // there's no way to get GITypeInfo for a method's instance parameter.
3594 : : // Instead, special-case the arguments here that would otherwise go through
3595 : : // the generic marshaller.
3596 : : // See: https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/334
3597 : 3609 : auto struct_info = interface_info->as<GI::InfoTag::STRUCT>();
3598 [ + + + + : 3609 : if (struct_info && struct_info->is_gtype_struct()) {
+ + ]
3599 : 461 : set_instance(new Arg::GTypeStructInstanceIn(), transfer);
3600 : 461 : return;
3601 : : }
3602 : :
3603 [ + - ]: 3148 : if (auto reg_info = interface_info->as<GI::InfoTag::REGISTERED_TYPE>()) {
3604 : 3148 : GType gtype = reg_info->gtype();
3605 : :
3606 [ + + - + : 3148 : if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
3607 : 128 : set_instance(new Arg::ParamInstanceIn(), transfer);
3608 : 128 : return;
3609 : : }
3610 [ + + ]: 3148 : }
3611 : :
3612 : 3020 : build_interface_in_arg<Arg::Kind::INSTANCE>(
3613 : 3020 : Argument::Init{nullptr, Argument::ABSENT, transfer,
3614 : : GjsArgumentFlags::NONE},
3615 : : *interface_info);
3616 [ + + + + ]: 4198 : }
3617 : :
3618 : 16 : static constexpr bool type_tag_is_scalar(GITypeTag tag) {
3619 [ + - + - : 16 : return GI_TYPE_TAG_IS_NUMERIC(tag) || tag == GI_TYPE_TAG_BOOLEAN ||
+ - - + ]
3620 : 16 : tag == GI_TYPE_TAG_GTYPE;
3621 : : }
3622 : :
3623 : 21010 : void ArgsCache::build_arg(uint8_t gi_index, GIDirection direction,
3624 : : const GI::ArgInfo arg,
3625 : : const GI::CallableInfo callable,
3626 : : bool* inc_counter_out) {
3627 : 21010 : g_assert(inc_counter_out && "forgot out parameter");
3628 : :
3629 : 21010 : GI::StackTypeInfo type_info;
3630 : 21010 : arg.load_type(&type_info);
3631 : :
3632 : 21010 : GjsArgumentFlags flags = GjsArgumentFlags::NONE;
3633 [ + + ]: 21010 : if (arg.may_be_null())
3634 : 4156 : flags |= GjsArgumentFlags::MAY_BE_NULL;
3635 [ + + ]: 21010 : if (arg.caller_allocates())
3636 : 19 : flags |= GjsArgumentFlags::CALLER_ALLOCATES;
3637 : :
3638 [ + + ]: 21010 : if (direction == GI_DIRECTION_IN)
3639 : 20077 : flags |= GjsArgumentFlags::SKIP_OUT;
3640 [ + + ]: 933 : else if (direction == GI_DIRECTION_OUT)
3641 : 819 : flags |= GjsArgumentFlags::SKIP_IN;
3642 : 21010 : *inc_counter_out = true;
3643 : :
3644 : 21010 : Argument::Init common_args{arg.name(), gi_index, arg.ownership_transfer(),
3645 : 21010 : flags};
3646 : :
3647 : 21010 : GITypeTag type_tag = type_info.tag();
3648 [ + + + + ]: 21829 : if (direction == GI_DIRECTION_OUT &&
3649 [ + + ]: 21829 : (flags & GjsArgumentFlags::CALLER_ALLOCATES)) {
3650 : 19 : size_t size = 0;
3651 : :
3652 [ + + ]: 19 : if (type_tag == GI_TYPE_TAG_ARRAY) {
3653 [ + + ]: 3 : switch (type_info.array_type()) {
3654 : 2 : case GI_ARRAY_TYPE_C: {
3655 : 2 : Maybe<size_t> n_elements = type_info.array_fixed_size();
3656 [ + - - + : 2 : if (!n_elements || *n_elements == 0)
- + ]
3657 : 0 : break;
3658 : :
3659 : 2 : GI::AutoTypeInfo element_type{type_info.element_type()};
3660 : 2 : size = gjs_type_get_element_size(element_type.tag(),
3661 : : element_type);
3662 : 2 : size *= *n_elements;
3663 : 2 : break;
3664 : 2 : }
3665 : 1 : default:
3666 : 1 : break;
3667 : : }
3668 [ + - + + : 16 : } else if (!type_tag_is_scalar(type_tag) && !type_info.is_pointer()) {
+ + ]
3669 : : // Scalar out parameters should not be annotated with
3670 : : // caller-allocates, which is for structured types that need to be
3671 : : // allocated in order for the function to fill them in.
3672 : 14 : size = gjs_type_get_element_size(type_tag, type_info);
3673 : : }
3674 : :
3675 [ + + ]: 19 : if (!size) {
3676 : 3 : set_argument(
3677 : 3 : new Arg::NotIntrospectable(OUT_CALLER_ALLOCATES_NON_STRUCT),
3678 : : common_args);
3679 : 3 : return;
3680 : : }
3681 : :
3682 [ + + ]: 16 : if (type_tag == GI_TYPE_TAG_INTERFACE) {
3683 : 14 : GI::AutoBaseInfo interface_info{type_info.interface()};
3684 : : // FIXME: What if it is not a registered type
3685 : : GType gtype =
3686 : 14 : interface_info.as<GI::InfoTag::REGISTERED_TYPE>()->gtype();
3687 [ + - + + : 14 : if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + ]
3688 : 10 : set_argument(
3689 : 10 : new Arg::BoxedCallerAllocatesOut(type_info, size, gtype),
3690 : : common_args);
3691 : 10 : return;
3692 : : }
3693 [ + + ]: 14 : }
3694 : :
3695 : 6 : set_argument(new Arg::CallerAllocatesOut(type_info, size), common_args);
3696 : 6 : return;
3697 : : }
3698 : :
3699 [ + + ]: 20991 : if (type_tag == GI_TYPE_TAG_INTERFACE) {
3700 : 7519 : GI::AutoBaseInfo interface_info{type_info.interface()};
3701 [ + + ]: 7519 : if (auto callback_info = interface_info.as<GI::InfoTag::CALLBACK>()) {
3702 [ - + ]: 1126 : if (direction != GI_DIRECTION_IN) {
3703 : : // Can't do callbacks for out or inout
3704 : 0 : set_argument(new Arg::NotIntrospectable(CALLBACK_OUT),
3705 : : common_args);
3706 : 0 : return;
3707 : : }
3708 : :
3709 [ + + + + ]: 1191 : if (strcmp(interface_info.name(), "DestroyNotify") == 0 &&
3710 [ + - ]: 65 : strcmp(interface_info.ns(), "GLib") == 0) {
3711 : : // We don't know (yet) what to do with GDestroyNotify appearing
3712 : : // before a callback. If the callback comes later in the
3713 : : // argument list, then the invalid argument will be
3714 : : // overwritten with the 'skipped' one. If no callback follows,
3715 : : // then this is probably an unsupported function, so the
3716 : : // function invocation code will check this and throw.
3717 : 65 : set_argument(
3718 : 65 : new Arg::NotIntrospectable(DESTROY_NOTIFY_NO_CALLBACK),
3719 : : common_args);
3720 : 65 : *inc_counter_out = false;
3721 : : } else {
3722 : 1061 : Maybe<unsigned> destroy_pos = arg.destroy_index();
3723 : 1061 : Maybe<unsigned> closure_pos = arg.closure_index();
3724 : 1061 : g_assert(destroy_pos.valueOr(0) <= Argument::MAX_ARGS &&
3725 : : "No more than 253 arguments allowed");
3726 : 1061 : g_assert(closure_pos.valueOr(0) <= Argument::MAX_ARGS &&
3727 : : "No more than 253 arguments allowed");
3728 : :
3729 [ + + ]: 1061 : if (destroy_pos)
3730 : 157 : set_skip_all(*destroy_pos);
3731 : :
3732 [ + + ]: 1061 : if (closure_pos)
3733 : 651 : set_skip_all(*closure_pos);
3734 : :
3735 [ + + + + : 1061 : if (destroy_pos && !closure_pos) {
+ + ]
3736 : 1 : set_argument(
3737 : 1 : new Arg::NotIntrospectable(DESTROY_NOTIFY_NO_USER_DATA),
3738 : : common_args);
3739 : 1 : return;
3740 : : }
3741 : :
3742 : 1060 : set_argument(new Arg::CallbackIn(*callback_info, closure_pos,
3743 : 2120 : destroy_pos, arg.scope()),
3744 : : common_args);
3745 : : }
3746 : :
3747 : 1125 : return;
3748 [ + + ]: 7519 : }
3749 [ + + ]: 7519 : }
3750 : :
3751 [ + + + + : 21042 : if (type_tag == GI_TYPE_TAG_ARRAY &&
+ + ]
3752 : 1177 : type_info.array_type() == GI_ARRAY_TYPE_C) {
3753 : 1151 : Maybe<unsigned> length_pos = type_info.array_length_index();
3754 : :
3755 [ + + ]: 1151 : if (length_pos) {
3756 : 963 : Argument* cached_length = argument(*length_pos);
3757 [ + + + + ]: 967 : bool skip_length = cached_length && !(cached_length->skip_in() &&
3758 [ + + ]: 4 : cached_length->skip_out());
3759 : :
3760 : 963 : set_array_argument(callable, gi_index, type_info, direction, arg,
3761 : 963 : flags, *length_pos);
3762 : :
3763 [ + + + + : 963 : if (*length_pos < gi_index && skip_length) {
+ + ]
3764 : : // we already collected length_pos, remove it
3765 : 180 : *inc_counter_out = false;
3766 : : }
3767 : :
3768 : 963 : return;
3769 : : }
3770 : : }
3771 : :
3772 [ + + ]: 18902 : if (direction == GI_DIRECTION_IN)
3773 : 18044 : build_normal_in_arg(gi_index, type_info, arg, flags);
3774 [ + + ]: 858 : else if (direction == GI_DIRECTION_INOUT)
3775 : 89 : build_normal_inout_arg(gi_index, type_info, arg, flags);
3776 : : else
3777 : 769 : build_normal_out_arg(gi_index, type_info, arg, flags);
3778 : :
3779 : 18902 : return;
3780 : : }
3781 : :
3782 : : } // namespace Gjs
|