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