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