Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2008 litl, LLC
4 : : // SPDX-FileCopyrightText: 2020 Canonical, Ltd.
5 : :
6 : : #include <config.h>
7 : :
8 : : #include <inttypes.h>
9 : : #include <stdint.h>
10 : : #include <string.h> // for strcmp, strlen, memcpy
11 : :
12 : : #include <array>
13 : : #include <string>
14 : : #include <utility> // for move
15 : :
16 : : #include <girepository.h>
17 : : #include <glib-object.h>
18 : : #include <glib.h>
19 : :
20 : : #include <js/Array.h>
21 : : #include <js/CharacterEncoding.h>
22 : : #include <js/Conversions.h>
23 : : #include <js/ErrorReport.h> // for JS_ReportOutOfMemory
24 : : #include <js/Exception.h>
25 : : #include <js/GCVector.h> // for RootedVector, MutableWrappedPtrOp...
26 : : #include <js/Id.h>
27 : : #include <js/PropertyAndElement.h> // for JS_GetElement, JS_HasPropertyById
28 : : #include <js/PropertyDescriptor.h> // for JSPROP_ENUMERATE
29 : : #include <js/RootingAPI.h>
30 : : #include <js/String.h>
31 : : #include <js/TypeDecls.h>
32 : : #include <js/Utility.h> // for UniqueChars
33 : : #include <js/Value.h>
34 : : #include <js/ValueArray.h>
35 : : #include <js/experimental/TypedData.h>
36 : : #include <jsapi.h> // for InformalValueTypeName, IdVector
37 : : #include <mozilla/Unused.h>
38 : :
39 : : #include "gi/arg-inl.h"
40 : : #include "gi/arg-types-inl.h"
41 : : #include "gi/arg.h"
42 : : #include "gi/boxed.h"
43 : : #include "gi/closure.h"
44 : : #include "gi/foreign.h"
45 : : #include "gi/fundamental.h"
46 : : #include "gi/gerror.h"
47 : : #include "gi/gtype.h"
48 : : #include "gi/info.h"
49 : : #include "gi/interface.h"
50 : : #include "gi/js-value-inl.h"
51 : : #include "gi/object.h"
52 : : #include "gi/param.h"
53 : : #include "gi/repo.h"
54 : : #include "gi/union.h"
55 : : #include "gi/value.h"
56 : : #include "gi/wrapperutils.h"
57 : : #include "gjs/atoms.h"
58 : : #include "gjs/auto.h"
59 : : #include "gjs/byteArray.h"
60 : : #include "gjs/context-private.h"
61 : : #include "gjs/enum-utils.h"
62 : : #include "gjs/gerror-result.h"
63 : : #include "gjs/jsapi-util.h"
64 : : #include "gjs/macros.h"
65 : : #include "util/log.h"
66 : :
67 : : // This file contains marshalling functions for GIArgument. It is divided into
68 : : // three sections, with some helper functions coming first.
69 : : // 1. "To" marshallers - JS::Value or other JS data structure to GIArgument or
70 : : // pointer, used for in arguments
71 : : // 2. "From" marshallers - GIArgument or pointer to JS::Value or other JS data
72 : : // structure, used for out arguments and return values
73 : : // 3. "Release" marshallers - used when cleaning up GIArguments after a C
74 : : // function call
75 : :
76 : : GJS_JSAPI_RETURN_CONVENTION static bool gjs_g_arg_release_internal(
77 : : JSContext*, GITransfer, GITypeInfo*, GITypeTag, GjsArgumentType,
78 : : GjsArgumentFlags, GIArgument*);
79 : : static void throw_invalid_argument(JSContext* cx, JS::HandleValue value,
80 : : GITypeInfo* arginfo, const char* arg_name,
81 : : GjsArgumentType arg_type);
82 : :
83 : 24 : bool _gjs_flags_value_is_valid(JSContext* cx, GType gtype, int64_t value) {
84 : : /* Do proper value check for flags with GType's */
85 [ + + ]: 24 : if (gtype != G_TYPE_NONE) {
86 : 23 : Gjs::AutoTypeClass<GFlagsClass> gflags_class{gtype};
87 : 23 : uint32_t tmpval = static_cast<uint32_t>(value);
88 : :
89 : : /* check all bits are valid bits for the flag and is a 32 bit flag*/
90 [ - + ]: 23 : if ((tmpval &= gflags_class->mask) != value) { /* Not a guint32 with invalid mask values*/
91 : 0 : gjs_throw(cx, "0x%" PRIx64 " is not a valid value for flags %s",
92 : : value, g_type_name(gtype));
93 : 0 : return false;
94 : : }
95 [ + - ]: 23 : }
96 : :
97 : 24 : return true;
98 : : }
99 : :
100 : : GJS_JSAPI_RETURN_CONVENTION
101 : 2114 : static bool _gjs_enum_value_is_valid(JSContext* cx, GIEnumInfo* enum_info,
102 : : int64_t value) {
103 : : bool found;
104 : : int n_values;
105 : : int i;
106 : :
107 : 2114 : n_values = g_enum_info_get_n_values(enum_info);
108 : 2114 : found = false;
109 : :
110 [ + - ]: 28740 : for (i = 0; i < n_values; ++i) {
111 : 28740 : GI::AutoValueInfo value_info{g_enum_info_get_value(enum_info, i)};
112 : 28740 : int64_t enum_value = g_value_info_get_value(value_info);
113 : :
114 [ + + ]: 28740 : if (enum_value == value) {
115 : 2114 : found = true;
116 : 2114 : break;
117 : : }
118 [ + + ]: 28740 : }
119 : :
120 [ - + ]: 2114 : if (!found) {
121 : 0 : gjs_throw(cx, "%" PRId64 " is not a valid value for enumeration %s",
122 : : value, g_base_info_get_name(enum_info));
123 : : }
124 : :
125 : 2114 : return found;
126 : : }
127 : :
128 : 2338 : [[nodiscard]] static bool _gjs_enum_uses_signed_type(GIEnumInfo* enum_info) {
129 [ + + ]: 2338 : switch (g_enum_info_get_storage_type(enum_info)) {
130 : 93 : case GI_TYPE_TAG_INT8:
131 : : case GI_TYPE_TAG_INT16:
132 : : case GI_TYPE_TAG_INT32:
133 : : case GI_TYPE_TAG_INT64:
134 : 93 : return true;
135 : 2245 : default:
136 : 2245 : return false;
137 : : }
138 : : }
139 : :
140 : : // This is hacky - g_function_info_invoke() and g_field_info_get/set_field()
141 : : // expect the enum value in gjs_arg_member<int>(arg) and depend on all flags and
142 : : // enumerations being passed on the stack in a 32-bit field. See FIXME comment
143 : : // in g_field_info_get_field(). The same assumption of enums cast to 32-bit
144 : : // signed integers is found in g_value_set_enum()/g_value_set_flags().
145 : 2338 : [[nodiscard]] int64_t _gjs_enum_from_int(GIEnumInfo* enum_info, int int_value) {
146 [ + + ]: 2338 : if (_gjs_enum_uses_signed_type (enum_info))
147 : 93 : return int64_t(int_value);
148 : : else
149 : 2245 : return int64_t(uint32_t(int_value));
150 : : }
151 : :
152 : : /* Here for symmetry, but result is the same for the two cases */
153 : 116 : [[nodiscard]] static int _gjs_enum_to_int(int64_t value) {
154 : 116 : return static_cast<int>(value);
155 : : }
156 : :
157 : : /* Check if an argument of the given needs to be released if we created it
158 : : * from a JS value to pass it into a function and aren't transferring ownership.
159 : : */
160 : 276 : [[nodiscard]] static bool type_needs_release(GITypeInfo* type_info,
161 : : GITypeTag type_tag) {
162 [ + + - ]: 276 : switch (type_tag) {
163 : 15 : case GI_TYPE_TAG_ARRAY:
164 : : case GI_TYPE_TAG_ERROR:
165 : : case GI_TYPE_TAG_FILENAME:
166 : : case GI_TYPE_TAG_GHASH:
167 : : case GI_TYPE_TAG_GLIST:
168 : : case GI_TYPE_TAG_GSLIST:
169 : : case GI_TYPE_TAG_UTF8:
170 : 15 : return true;
171 : :
172 : 261 : case GI_TYPE_TAG_INTERFACE: {
173 : : GI::AutoBaseInfo interface_info{
174 : 261 : g_type_info_get_interface(type_info)};
175 : 261 : g_assert(interface_info != nullptr);
176 : :
177 [ + - + + : 261 : if (!GI_IS_REGISTERED_TYPE_INFO(interface_info))
+ + + - +
+ - + - -
- - - + ]
178 : 0 : return false;
179 : :
180 : 261 : GType gtype = g_registered_type_info_get_g_type(interface_info);
181 : :
182 [ + - - + : 261 : if (g_type_is_a(gtype, G_TYPE_CLOSURE))
- + ]
183 : 0 : return true;
184 [ + + - + : 261 : else if (g_type_is_a(gtype, G_TYPE_VALUE))
+ + ]
185 : 11 : return true;
186 : : else
187 : 250 : return false;
188 : 261 : }
189 : :
190 : 0 : default:
191 : 0 : return false;
192 : : }
193 : : }
194 : :
195 : 521 : [[nodiscard]] static inline bool is_string_type(GITypeTag tag) {
196 [ + + + + ]: 521 : return tag == GI_TYPE_TAG_FILENAME || tag == GI_TYPE_TAG_UTF8;
197 : : }
198 : :
199 : : /* Check if an argument of the given needs to be released if we obtained it
200 : : * from out argument (or the return value), and we're transferring ownership
201 : : */
202 : 15 : [[nodiscard]] static bool type_needs_out_release(GITypeInfo* type_info,
203 : : GITypeTag type_tag) {
204 [ + + - ]: 15 : switch (type_tag) {
205 : 9 : case GI_TYPE_TAG_ARRAY:
206 : : case GI_TYPE_TAG_ERROR:
207 : : case GI_TYPE_TAG_FILENAME:
208 : : case GI_TYPE_TAG_GHASH:
209 : : case GI_TYPE_TAG_GLIST:
210 : : case GI_TYPE_TAG_GSLIST:
211 : : case GI_TYPE_TAG_UTF8:
212 : 9 : return true;
213 : :
214 : 6 : case GI_TYPE_TAG_INTERFACE: {
215 : : GI::AutoBaseInfo interface_info{
216 : 6 : g_type_info_get_interface(type_info)};
217 : :
218 [ + + ]: 6 : if (GI_IS_OBJECT_INFO(interface_info))
219 : 2 : return true;
220 [ - + - - : 4 : if (GI_IS_STRUCT_INFO(interface_info) ||
+ - ]
221 : 0 : GI_IS_UNION_INFO(interface_info))
222 : 4 : return g_type_info_is_pointer(type_info);
223 : :
224 : 0 : return false;
225 : 6 : }
226 : :
227 : 0 : default:
228 : 0 : return false;
229 : : }
230 : : }
231 : :
232 : : ///// "TO" MARSHALLERS /////////////////////////////////////////////////////////
233 : : // These marshaller functions are responsible for converting JS values to the
234 : : // required GIArgument type, for the in parameters of a C function call.
235 : :
236 : : template <typename T>
237 : 0 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_to_g_list(
238 : : JSContext* cx, JS::HandleValue value, GITypeInfo* type_info,
239 : : GITransfer transfer, const char* arg_name, GjsArgumentType arg_type,
240 : : T** list_p) {
241 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
242 : :
243 : : // While a list can be NULL in C, that means empty array in JavaScript, it
244 : : // doesn't mean null in JavaScript.
245 : : bool is_array;
246 [ # # ]: 0 : if (!JS::IsArrayObject(cx, value, &is_array))
247 : 0 : return false;
248 [ # # ]: 0 : if (!is_array) {
249 : 0 : throw_invalid_argument(cx, value, type_info, arg_name, arg_type);
250 : 0 : return false;
251 : : }
252 : :
253 : 0 : JS::RootedObject array_obj(cx, &value.toObject());
254 : :
255 : : uint32_t length;
256 [ # # ]: 0 : if (!JS::GetArrayLength(cx, array_obj, &length)) {
257 : 0 : throw_invalid_argument(cx, value, type_info, arg_name, arg_type);
258 : 0 : return false;
259 : : }
260 : :
261 : 0 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
262 : 0 : g_assert(param_info);
263 : :
264 : 0 : GITypeTag element_tag = g_type_info_get_tag(param_info);
265 : 0 : g_assert(!GI_TYPE_TAG_IS_BASIC(element_tag) &&
266 : : "use basic_array_to_linked_list() instead");
267 : :
268 [ # # ]: 0 : if (transfer == GI_TRANSFER_CONTAINER) {
269 [ # # ]: 0 : if (type_needs_release(param_info, element_tag)) {
270 : : /* FIXME: to make this work, we'd have to keep a list of temporary
271 : : * GIArguments for the function call so we could free them after
272 : : * the surrounding container had been freed by the callee.
273 : : */
274 : 0 : gjs_throw(cx, "Container transfer for in parameters not supported");
275 : 0 : return false;
276 : : }
277 : :
278 : 0 : transfer = GI_TRANSFER_NOTHING;
279 : : }
280 : :
281 : 0 : JS::RootedObject array(cx, value.toObjectOrNull());
282 : 0 : JS::RootedValue elem(cx);
283 : 0 : T* list = nullptr;
284 : :
285 [ # # ]: 0 : for (size_t i = 0; i < length; ++i) {
286 : 0 : elem = JS::UndefinedValue();
287 [ # # ]: 0 : if (!JS_GetElement(cx, array, i, &elem)) {
288 : 0 : gjs_throw(cx, "Missing array element %zu", i);
289 : 0 : return false;
290 : : }
291 : :
292 : : /* FIXME we don't know if the list elements can be NULL.
293 : : * gobject-introspection needs to tell us this.
294 : : * Always say they can't for now.
295 : : */
296 : : GIArgument elem_arg;
297 [ # # ]: 0 : if (!gjs_value_to_gi_argument(cx, elem, param_info,
298 : : GJS_ARGUMENT_LIST_ELEMENT, transfer,
299 : : &elem_arg)) {
300 : 0 : return false;
301 : : }
302 : :
303 : : void* hash_pointer =
304 : 0 : g_type_info_hash_pointer_from_argument(param_info, &elem_arg);
305 : :
306 : : if constexpr (std::is_same_v<T, GList>)
307 : 0 : list = g_list_prepend(list, hash_pointer);
308 : : else if constexpr (std::is_same_v<T, GSList>)
309 : 0 : list = g_slist_prepend(list, hash_pointer);
310 : : }
311 : :
312 : : if constexpr (std::is_same_v<T, GList>)
313 : 0 : list = g_list_reverse(list);
314 : : else if constexpr (std::is_same_v<T, GSList>)
315 : 0 : list = g_slist_reverse(list);
316 : :
317 : 0 : *list_p = list;
318 : :
319 : 0 : return true;
320 : 0 : }
321 : :
322 : 13 : [[nodiscard]] static GHashTable* create_hash_table_for_key_type(
323 : : GITypeTag key_type) {
324 : : /* Don't use key/value destructor functions here, because we can't
325 : : * construct correct ones in general if the value type is complex.
326 : : * Rely on the type-aware gi_argument_release functions. */
327 [ + + ]: 13 : if (is_string_type(key_type))
328 : 10 : return g_hash_table_new(g_str_hash, g_str_equal);
329 : 3 : return g_hash_table_new(NULL, NULL);
330 : : }
331 : :
332 : : template <typename IntTag>
333 : 11 : GJS_JSAPI_RETURN_CONVENTION static bool hashtable_int_key(JSContext* cx,
334 : : JS::HandleValue value,
335 : : void** pointer_out) {
336 : : using IntType = Gjs::Tag::RealT<IntTag>;
337 : : static_assert(std::is_integral_v<IntType>, "Need an integer");
338 : 11 : bool out_of_range = false;
339 : :
340 : : Gjs::Tag::JSValueContainingT<IntTag> i;
341 [ - + ]: 11 : if (!Gjs::js_value_to_c_checked<IntType>(cx, value, &i, &out_of_range))
342 : 0 : return false;
343 : :
344 [ - + ]: 11 : if (out_of_range) {
345 : 0 : gjs_throw(cx, "value is out of range for hash table key of type %s",
346 : : Gjs::static_type_name<IntTag>());
347 : : }
348 : :
349 : 11 : *pointer_out = gjs_int_to_pointer<IntType>(i);
350 : :
351 : 11 : return true;
352 : : }
353 : :
354 : : /* Converts a JS::Value to a GHashTable key, stuffing it into @pointer_out if
355 : : * possible, otherwise giving the location of an allocated key in @pointer_out.
356 : : */
357 : : GJS_JSAPI_RETURN_CONVENTION
358 : 48 : static bool value_to_ghashtable_key(JSContext* cx, JS::HandleValue value,
359 : : GITypeTag type_tag, void** pointer_out) {
360 : 48 : bool unsupported = false;
361 : :
362 : 48 : g_assert((value.isString() || value.isInt32()) &&
363 : : "keys from JS_Enumerate must be non-symbol property keys");
364 : :
365 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
366 : : "Converting JS::Value to GHashTable key %s",
367 : : g_type_tag_to_string(type_tag));
368 : :
369 [ - - - - : 48 : switch (type_tag) {
+ - - - -
+ - - ]
370 : 0 : case GI_TYPE_TAG_BOOLEAN:
371 : : /* This doesn't seem particularly useful, but it's easy */
372 : 0 : *pointer_out = gjs_int_to_pointer(JS::ToBoolean(value));
373 : 0 : break;
374 : :
375 : 0 : case GI_TYPE_TAG_UNICHAR:
376 [ # # ]: 0 : if (value.isInt32()) {
377 : 0 : *pointer_out = gjs_int_to_pointer(value.toInt32());
378 : : } else {
379 : : uint32_t ch;
380 [ # # ]: 0 : if (!gjs_unichar_from_string(cx, value, &ch))
381 : 0 : return false;
382 : 0 : *pointer_out = gjs_int_to_pointer(ch);
383 : : }
384 : 0 : break;
385 : :
386 : 0 : case GI_TYPE_TAG_INT8:
387 [ # # ]: 0 : if (!hashtable_int_key<int8_t>(cx, value, pointer_out))
388 : 0 : return false;
389 : 0 : break;
390 : :
391 : 0 : case GI_TYPE_TAG_INT16:
392 [ # # ]: 0 : if (!hashtable_int_key<int16_t>(cx, value, pointer_out))
393 : 0 : return false;
394 : 0 : break;
395 : :
396 : 11 : case GI_TYPE_TAG_INT32:
397 [ - + ]: 11 : if (!hashtable_int_key<int32_t>(cx, value, pointer_out))
398 : 0 : return false;
399 : 11 : break;
400 : :
401 : 0 : case GI_TYPE_TAG_UINT8:
402 [ # # ]: 0 : if (!hashtable_int_key<uint8_t>(cx, value, pointer_out))
403 : 0 : return false;
404 : 0 : break;
405 : :
406 : 0 : case GI_TYPE_TAG_UINT16:
407 [ # # ]: 0 : if (!hashtable_int_key<uint16_t>(cx, value, pointer_out))
408 : 0 : return false;
409 : 0 : break;
410 : :
411 : 0 : case GI_TYPE_TAG_UINT32:
412 [ # # ]: 0 : if (!hashtable_int_key<uint32_t>(cx, value, pointer_out))
413 : 0 : return false;
414 : 0 : break;
415 : :
416 : 0 : case GI_TYPE_TAG_FILENAME: {
417 : 0 : Gjs::AutoChar cstr;
418 : 0 : JS::RootedValue str_val(cx, value);
419 [ # # ]: 0 : if (!str_val.isString()) {
420 : 0 : JS::RootedString str(cx, JS::ToString(cx, str_val));
421 : 0 : str_val.setString(str);
422 : 0 : }
423 [ # # ]: 0 : if (!gjs_string_to_filename(cx, str_val, &cstr))
424 : 0 : return false;
425 : 0 : *pointer_out = cstr.release();
426 : 0 : break;
427 [ # # # # ]: 0 : }
428 : :
429 : 37 : case GI_TYPE_TAG_UTF8: {
430 : 37 : JS::RootedString str(cx);
431 [ + + ]: 37 : if (!value.isString())
432 : 21 : str = JS::ToString(cx, value);
433 : : else
434 : 16 : str = value.toString();
435 : :
436 : 37 : JS::UniqueChars cstr(JS_EncodeStringToUTF8(cx, str));
437 [ - + ]: 37 : if (!cstr)
438 : 0 : return false;
439 : 37 : *pointer_out = g_strdup(cstr.get());
440 : 37 : break;
441 [ - + - + ]: 74 : }
442 : :
443 : 0 : case GI_TYPE_TAG_FLOAT:
444 : : case GI_TYPE_TAG_DOUBLE:
445 : : case GI_TYPE_TAG_INT64:
446 : : case GI_TYPE_TAG_UINT64:
447 : : // FIXME: The above four could be supported, but are currently not. The ones
448 : : // below cannot be key types in a regular JS object; we would need to allow
449 : : // marshalling Map objects into GHashTables to support those, as well as
450 : : // refactoring this function to take GITypeInfo* and splitting out the
451 : : // marshalling for basic types into a different function.
452 : : case GI_TYPE_TAG_VOID:
453 : : case GI_TYPE_TAG_GTYPE:
454 : : case GI_TYPE_TAG_ERROR:
455 : : case GI_TYPE_TAG_INTERFACE:
456 : : case GI_TYPE_TAG_GLIST:
457 : : case GI_TYPE_TAG_GSLIST:
458 : : case GI_TYPE_TAG_GHASH:
459 : : case GI_TYPE_TAG_ARRAY:
460 : 0 : unsupported = true;
461 : 0 : break;
462 : :
463 : 0 : default:
464 : 0 : g_warning("Unhandled type %s for GHashTable key conversion",
465 : : g_type_tag_to_string(type_tag));
466 : 0 : unsupported = true;
467 : 0 : break;
468 : : }
469 : :
470 [ - + ]: 48 : if (G_UNLIKELY(unsupported)) {
471 : 0 : gjs_throw(cx, "Type %s not supported for hash table keys",
472 : : g_type_tag_to_string(type_tag));
473 : 0 : return false;
474 : : }
475 : :
476 : 48 : return true;
477 : : }
478 : :
479 : : template <typename TAG>
480 : 16 : [[nodiscard]] static Gjs::Tag::RealT<TAG>* heap_value_new_from_arg(
481 : : GIArgument* val_arg) {
482 : 16 : auto* heap_val = g_new(Gjs::Tag::RealT<TAG>, 1);
483 : 16 : *heap_val = gjs_arg_get<TAG>(val_arg);
484 : :
485 : 16 : return heap_val;
486 : : }
487 : :
488 : : GJS_JSAPI_RETURN_CONVENTION
489 : 1 : static bool gjs_object_to_g_hash(JSContext* context, JS::HandleObject props,
490 : : GITypeInfo* type_info, GITransfer transfer,
491 : : GHashTable** hash_p) {
492 : : size_t id_ix, id_len;
493 : :
494 : 1 : g_assert(props && "Property bag cannot be null");
495 : :
496 : 1 : GI::AutoTypeInfo key_param_info{g_type_info_get_param_type(type_info, 0)};
497 : 1 : GI::AutoTypeInfo val_param_info{g_type_info_get_param_type(type_info, 1)};
498 : 1 : GITypeTag key_tag = g_type_info_get_tag(key_param_info);
499 : 1 : GITypeTag val_type = g_type_info_get_tag(val_param_info);
500 : :
501 : 1 : g_assert(
502 : : (!GI_TYPE_TAG_IS_BASIC(key_tag) || !GI_TYPE_TAG_IS_BASIC(val_type)) &&
503 : : "use gjs_value_to_basic_ghash_gi_argument() instead");
504 : :
505 [ - + ]: 1 : if (transfer == GI_TRANSFER_CONTAINER) {
506 [ # # # # : 0 : if (type_needs_release (key_param_info, g_type_info_get_tag(key_param_info)) ||
# # ]
507 : 0 : type_needs_release (val_param_info, g_type_info_get_tag(val_param_info))) {
508 : : /* FIXME: to make this work, we'd have to keep a list of temporary
509 : : * GIArguments for the function call so we could free them after
510 : : * the surrounding container had been freed by the callee.
511 : : */
512 : 0 : gjs_throw(context,
513 : : "Container transfer for in parameters not supported");
514 : 0 : return false;
515 : : }
516 : :
517 : 0 : transfer = GI_TRANSFER_NOTHING;
518 : : }
519 : :
520 : 1 : JS::Rooted<JS::IdVector> ids(context, context);
521 [ - + ]: 1 : if (!JS_Enumerate(context, props, &ids))
522 : 0 : return false;
523 : :
524 : 34 : Gjs::AutoPointer<GHashTable, GHashTable, g_hash_table_destroy> result{
525 : 1 : create_hash_table_for_key_type(key_tag)};
526 : :
527 : 1 : JS::RootedValue key_js(context), val_js(context);
528 : 1 : JS::RootedId cur_id(context);
529 [ + + ]: 4 : for (id_ix = 0, id_len = ids.length(); id_ix < id_len; ++id_ix) {
530 : 3 : cur_id = ids[id_ix];
531 : : gpointer key_ptr, val_ptr;
532 : 3 : GIArgument val_arg = { 0 };
533 : :
534 : 3 : if (!JS_IdToValue(context, cur_id, &key_js) ||
535 : : // Type check key type.
536 [ + - ]: 3 : !value_to_ghashtable_key(context, key_js, key_tag, &key_ptr) ||
537 [ + - + - ]: 9 : !JS_GetPropertyById(context, props, cur_id, &val_js) ||
538 : : // Type check and convert value to a C type
539 [ - + - + ]: 6 : !gjs_value_to_gi_argument(context, val_js, val_param_info, nullptr,
540 : : GJS_ARGUMENT_HASH_ELEMENT, transfer,
541 : : GjsArgumentFlags::MAY_BE_NULL, &val_arg))
542 : 0 : return false;
543 : :
544 : : /* Use heap-allocated values for types that don't fit in a pointer */
545 [ - + ]: 3 : if (val_type == GI_TYPE_TAG_INT64) {
546 : 0 : val_ptr = heap_value_new_from_arg<int64_t>(&val_arg);
547 [ - + ]: 3 : } else if (val_type == GI_TYPE_TAG_UINT64) {
548 : 0 : val_ptr = heap_value_new_from_arg<uint64_t>(&val_arg);
549 [ - + ]: 3 : } else if (val_type == GI_TYPE_TAG_FLOAT) {
550 : 0 : val_ptr = heap_value_new_from_arg<float>(&val_arg);
551 [ - + ]: 3 : } else if (val_type == GI_TYPE_TAG_DOUBLE) {
552 : 0 : val_ptr = heap_value_new_from_arg<double>(&val_arg);
553 : : } else {
554 : : // Other types are simply stuffed inside the pointer
555 : 3 : val_ptr = g_type_info_hash_pointer_from_argument(val_param_info,
556 : : &val_arg);
557 : : }
558 : :
559 : : #if __GNUC__ >= 8 // clang-format off
560 : : _Pragma("GCC diagnostic push")
561 : : _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
562 : : #endif
563 : : // The compiler isn't smart enough to figure out that key_ptr will
564 : : // always be initialized if value_to_ghashtable_key() returns true.
565 : 3 : g_hash_table_insert(result, key_ptr, val_ptr);
566 : : #if __GNUC__ >= 8
567 : : _Pragma("GCC diagnostic pop")
568 : : #endif // clang-format on
569 : : }
570 : :
571 : 1 : *hash_p = result.release();
572 : 1 : return true;
573 : 1 : }
574 : :
575 : : template <typename T>
576 : 424 : [[nodiscard]] constexpr T* array_allocate(size_t length) {
577 : : if constexpr (std::is_same_v<T, char*>)
578 : 108 : return g_new0(char*, length);
579 : :
580 : 316 : T* array = g_new(T, length);
581 : 316 : array[length - 1] = {0};
582 : 316 : return array;
583 : : }
584 : :
585 : : template <typename TAG>
586 : 576 : GJS_JSAPI_RETURN_CONVENTION static bool js_value_to_c_strict(
587 : : JSContext* cx, JS::HandleValue value, Gjs::Tag::RealT<TAG>* out) {
588 : : if constexpr (Gjs::type_has_js_getter<TAG,
589 : : Gjs::HolderMode::ContainingType>())
590 : 520 : return Gjs::js_value_to_c<TAG>(cx, value, out);
591 : :
592 : : Gjs::Tag::JSValueContainingT<TAG> v;
593 : 56 : bool ret = Gjs::js_value_to_c<TAG>(cx, value, &v);
594 : 56 : *out = v;
595 : :
596 : 56 : return ret;
597 : : }
598 : :
599 : : template <typename T>
600 : 176 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_to_auto_array(
601 : : JSContext* cx, JS::Value array_value, size_t length, void** arr_p) {
602 : : using RealT = Gjs::Tag::RealT<T>;
603 : :
604 : 176 : JS::RootedObject array(cx, array_value.toObjectOrNull());
605 : 176 : JS::RootedValue elem(cx);
606 : :
607 : : // Add one so we're always zero terminated
608 : 176 : Gjs::SmartPointer<RealT> result{array_allocate<RealT>(length + 1)};
609 : :
610 [ + + ]: 748 : for (size_t i = 0; i < length; ++i) {
611 : 576 : elem = JS::UndefinedValue();
612 : :
613 [ - + ]: 576 : if (!JS_GetElement(cx, array, i, &elem)) {
614 : 0 : gjs_throw(cx, "Missing array element %" G_GSIZE_FORMAT, i);
615 : 0 : return false;
616 : : }
617 : :
618 [ + + ]: 576 : if (!js_value_to_c_strict<T>(cx, elem, &result[i])) {
619 : 4 : gjs_throw(cx, "Invalid element in %s array",
620 : : Gjs::static_type_name<T>());
621 : 4 : return false;
622 : : }
623 : : }
624 : :
625 : 172 : *arr_p = result.release();
626 : :
627 : 172 : return true;
628 : 176 : }
629 : :
630 : : bool
631 : 108 : gjs_array_to_strv(JSContext *context,
632 : : JS::Value array_value,
633 : : unsigned int length,
634 : : void **arr_p)
635 : : {
636 : 108 : return gjs_array_to_auto_array<char*>(context, array_value, length, arr_p);
637 : : }
638 : :
639 : : GJS_JSAPI_RETURN_CONVENTION
640 : 18 : static bool gjs_string_to_intarray(JSContext* context, JS::HandleString str,
641 : : GITypeTag element_type, void** arr_p,
642 : : size_t* length) {
643 : : char16_t *result16;
644 : :
645 [ + + + + ]: 18 : switch (element_type) {
646 : 13 : case GI_TYPE_TAG_INT8:
647 : : case GI_TYPE_TAG_UINT8: {
648 : 13 : JS::UniqueChars result;
649 [ - + ]: 13 : if (!gjs_string_to_utf8_n(context, str, &result, length))
650 : 0 : return false;
651 : :
652 : 13 : *arr_p = Gjs::js_chars_to_glib(std::move(result)).release();
653 : 13 : return true;
654 : 13 : }
655 : :
656 : 2 : case GI_TYPE_TAG_INT16:
657 : : case GI_TYPE_TAG_UINT16: {
658 [ - + ]: 2 : if (!gjs_string_get_char16_data(context, str, &result16, length))
659 : 0 : return false;
660 : 2 : *arr_p = result16;
661 : 2 : return true;
662 : : }
663 : :
664 : 2 : case GI_TYPE_TAG_UNICHAR: {
665 : : gunichar* result_ucs4;
666 [ - + ]: 2 : if (!gjs_string_to_ucs4(context, str, &result_ucs4, length))
667 : 0 : return false;
668 : 2 : *arr_p = result_ucs4;
669 : 2 : return true;
670 : : }
671 : :
672 : 1 : default:
673 : : /* can't convert a string to this type */
674 : 1 : gjs_throw(context, "Cannot convert string to array of '%s'",
675 : : g_type_tag_to_string(element_type));
676 : 1 : return false;
677 : : }
678 : : }
679 : :
680 : : GJS_JSAPI_RETURN_CONVENTION
681 : 1 : static bool array_to_basic_c_array(JSContext* cx, JS::HandleValue v_array,
682 : : size_t length, GITypeTag element_tag,
683 : : void** array_out) {
684 : : // Always one extra element, to cater for null terminated arrays
685 : 1 : Gjs::AutoPointer<void*> array{array_allocate<void*>(length + 1)};
686 : :
687 : 1 : JS::RootedObject array_obj{cx, &v_array.toObject()};
688 : 1 : JS::RootedValue elem{cx};
689 [ + + ]: 3 : for (size_t ix = 0; ix < length; ix++) {
690 : : GIArgument arg;
691 : 2 : gjs_arg_unset(&arg);
692 : :
693 : 2 : elem.setUndefined();
694 [ - + ]: 2 : if (!JS_GetElement(cx, array_obj, ix, &elem)) {
695 : 0 : gjs_throw(cx, "Missing array element %zu", ix);
696 : 0 : return false;
697 : : }
698 : :
699 [ - + ]: 2 : if (!gjs_value_to_basic_gi_argument(cx, elem, element_tag, &arg,
700 : : nullptr, GJS_ARGUMENT_ARRAY_ELEMENT,
701 : : GjsArgumentFlags::NONE)) {
702 : 0 : gjs_throw(cx, "Invalid element in array");
703 : 0 : return false;
704 : : }
705 : :
706 : 2 : array[ix] = gjs_arg_get<void*>(&arg);
707 : : }
708 : :
709 : 1 : *array_out = array.release();
710 : 1 : return true;
711 : 1 : }
712 : :
713 : : GJS_JSAPI_RETURN_CONVENTION
714 : : static bool
715 : 247 : gjs_array_to_ptrarray(JSContext *context,
716 : : JS::Value array_value,
717 : : unsigned int length,
718 : : GITransfer transfer,
719 : : GITypeInfo *param_info,
720 : : void **arr_p)
721 : : {
722 : : unsigned int i;
723 : 247 : JS::RootedObject array_obj(context, array_value.toObjectOrNull());
724 : 247 : JS::RootedValue elem(context);
725 : :
726 : : /* Always one extra element, to cater for null terminated arrays */
727 : 247 : Gjs::AutoPointer<void*> array{array_allocate<void*>(length + 1)};
728 : :
729 [ + + ]: 665 : for (i = 0; i < length; i++) {
730 : : GIArgument arg;
731 : 418 : gjs_arg_unset(&arg);
732 : :
733 : 418 : elem = JS::UndefinedValue();
734 [ - + ]: 418 : if (!JS_GetElement(context, array_obj, i, &elem)) {
735 : 0 : gjs_throw(context,
736 : : "Missing array element %u",
737 : : i);
738 : 0 : return false;
739 : : }
740 : :
741 [ - + ]: 418 : if (!gjs_value_to_gi_argument(context, elem, param_info,
742 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
743 : : &arg)) {
744 : 0 : gjs_throw(context,
745 : : "Invalid element in array");
746 : 0 : return false;
747 : : }
748 : :
749 : 418 : array[i] = gjs_arg_get<void*>(&arg);
750 : : }
751 : :
752 : 247 : *arr_p = array.release();
753 : 247 : return true;
754 : 247 : }
755 : :
756 : : GJS_JSAPI_RETURN_CONVENTION
757 : 4 : static bool gjs_array_to_flat_array(JSContext* cx, JS::HandleValue array_value,
758 : : unsigned length, GITypeInfo* param_info,
759 : : size_t param_size, void** arr_p) {
760 : 4 : g_assert((param_size > 0) &&
761 : : "Only flat arrays of elements of known size are supported");
762 : :
763 : 4 : Gjs::AutoPointer<uint8_t> flat_array{g_new0(uint8_t, param_size * length)};
764 : :
765 : 4 : JS::RootedObject array(cx, &array_value.toObject());
766 : 4 : JS::RootedValue elem(cx);
767 [ + + ]: 9 : for (unsigned i = 0; i < length; i++) {
768 : 5 : elem = JS::UndefinedValue();
769 : :
770 [ - + ]: 5 : if (!JS_GetElement(cx, array, i, &elem)) {
771 : 0 : gjs_throw(cx, "Missing array element %u", i);
772 : 0 : return false;
773 : : }
774 : :
775 : : GIArgument arg;
776 [ - + ]: 5 : if (!gjs_value_to_gi_argument(cx, elem, param_info,
777 : : GJS_ARGUMENT_ARRAY_ELEMENT,
778 : : GI_TRANSFER_NOTHING, &arg))
779 : 0 : return false;
780 : :
781 : 5 : memcpy(&flat_array[param_size * i], gjs_arg_get<void*>(&arg),
782 : : param_size);
783 : : }
784 : :
785 : 4 : *arr_p = flat_array.release();
786 : 4 : return true;
787 : 4 : }
788 : :
789 : 10 : [[nodiscard]] static bool is_gvalue(GIBaseInfo* info) {
790 [ + - + - : 10 : if (GI_IS_REGISTERED_TYPE_INFO(info)) {
+ - + - +
+ - + - -
- - + - ]
791 : 10 : GType gtype = g_registered_type_info_get_g_type(info);
792 [ + + - + ]: 10 : return g_type_is_a(gtype, G_TYPE_VALUE);
793 : : }
794 : :
795 : 0 : return false;
796 : : }
797 : :
798 : : GJS_JSAPI_RETURN_CONVENTION
799 : 164 : static bool gjs_array_to_basic_array(JSContext* cx, JS::HandleValue v_array,
800 : : size_t length,
801 : : GITypeTag element_storage_type,
802 : : void** array_out) {
803 : 164 : g_assert(GI_TYPE_TAG_IS_BASIC(element_storage_type));
804 : :
805 [ + + + + : 164 : switch (element_storage_type) {
+ + - + +
+ + + + -
+ - - ]
806 : 99 : case GI_TYPE_TAG_UTF8:
807 : 99 : return gjs_array_to_strv(cx, v_array, length, array_out);
808 : 1 : case GI_TYPE_TAG_FILENAME:
809 : 1 : return array_to_basic_c_array(cx, v_array, length,
810 : 1 : element_storage_type, array_out);
811 : 3 : case GI_TYPE_TAG_BOOLEAN:
812 : 3 : return gjs_array_to_auto_array<Gjs::Tag::GBoolean>(
813 : 3 : cx, v_array, length, array_out);
814 : 2 : case GI_TYPE_TAG_UNICHAR:
815 : 2 : return gjs_array_to_auto_array<char32_t>(cx, v_array, length,
816 : 2 : array_out);
817 : 7 : case GI_TYPE_TAG_UINT8:
818 : 7 : return gjs_array_to_auto_array<uint8_t>(cx, v_array, length,
819 : 7 : array_out);
820 : 2 : case GI_TYPE_TAG_INT8:
821 : 2 : return gjs_array_to_auto_array<int8_t>(cx, v_array, length,
822 : 2 : array_out);
823 : 0 : case GI_TYPE_TAG_UINT16:
824 : 0 : return gjs_array_to_auto_array<uint16_t>(cx, v_array, length,
825 : 0 : array_out);
826 : 2 : case GI_TYPE_TAG_INT16:
827 : 2 : return gjs_array_to_auto_array<int16_t>(cx, v_array, length,
828 : 2 : array_out);
829 : 3 : case GI_TYPE_TAG_UINT32:
830 : 3 : return gjs_array_to_auto_array<uint32_t>(cx, v_array, length,
831 : 3 : array_out);
832 : 37 : case GI_TYPE_TAG_INT32:
833 : 37 : return gjs_array_to_auto_array<int32_t>(cx, v_array, length,
834 : 37 : array_out);
835 : 2 : case GI_TYPE_TAG_INT64:
836 : 2 : return gjs_array_to_auto_array<int64_t>(cx, v_array, length,
837 : 2 : array_out);
838 : 2 : case GI_TYPE_TAG_UINT64:
839 : 2 : return gjs_array_to_auto_array<uint64_t>(cx, v_array, length,
840 : 2 : array_out);
841 : 1 : case GI_TYPE_TAG_FLOAT:
842 : 1 : return gjs_array_to_auto_array<float>(cx, v_array, length,
843 : 1 : array_out);
844 : 0 : case GI_TYPE_TAG_DOUBLE:
845 : 0 : return gjs_array_to_auto_array<double>(cx, v_array, length,
846 : 0 : array_out);
847 : 3 : case GI_TYPE_TAG_GTYPE:
848 : 3 : return gjs_array_to_auto_array<Gjs::Tag::GType>(cx, v_array, length,
849 : 3 : array_out);
850 : 0 : case GI_TYPE_TAG_VOID:
851 : 0 : gjs_throw(cx, "Unhandled array element type %d",
852 : : element_storage_type);
853 : 0 : return false;
854 : 0 : default:
855 : : g_assert_not_reached();
856 : : }
857 : : }
858 : :
859 : : GJS_JSAPI_RETURN_CONVENTION
860 : 257 : static bool gjs_array_to_array(JSContext* context, JS::HandleValue array_value,
861 : : size_t length, GITransfer transfer,
862 : : GITypeInfo* param_info, void** arr_p) {
863 : 257 : GITypeTag element_type = g_type_info_get_storage_type(param_info);
864 : :
865 [ + + - + ]: 257 : if (GI_TYPE_TAG_IS_BASIC(element_type)) {
866 : 2 : return gjs_array_to_basic_array(context, array_value, length,
867 : 2 : element_type, arr_p);
868 : : }
869 : :
870 [ + + - ]: 255 : switch (element_type) {
871 : 243 : case GI_TYPE_TAG_INTERFACE:
872 [ + + ]: 243 : if (!g_type_info_is_pointer(param_info)) {
873 : : GI::AutoBaseInfo interface_info{
874 : 9 : g_type_info_get_interface(param_info)};
875 [ + + ]: 9 : if (is_gvalue(interface_info)) {
876 : : // Special case for GValue "flat arrays", this could also
877 : : // using the generic case, but if we do so we're leaking atm.
878 : 4 : return gjs_array_to_auto_array<GValue>(context, array_value,
879 : 4 : length, arr_p);
880 : : }
881 : :
882 : 5 : size_t element_size = gjs_type_get_element_size(
883 : : g_type_info_get_tag(param_info), param_info);
884 : :
885 [ + + ]: 5 : if (element_size) {
886 : 4 : return gjs_array_to_flat_array(context, array_value, length,
887 : 4 : param_info, element_size, arr_p);
888 : : }
889 [ + + ]: 9 : }
890 : : [[fallthrough]];
891 : : case GI_TYPE_TAG_ARRAY:
892 : : case GI_TYPE_TAG_GLIST:
893 : : case GI_TYPE_TAG_GSLIST:
894 : : case GI_TYPE_TAG_GHASH:
895 : : case GI_TYPE_TAG_ERROR:
896 [ + + ]: 247 : return gjs_array_to_ptrarray(context,
897 : : array_value,
898 : : length,
899 : : transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer,
900 : : param_info,
901 : 247 : arr_p);
902 : 0 : default:
903 : : // Basic types already handled in gjs_array_to_basic_array()
904 : 0 : gjs_throw(context, "Unhandled array element type %d", element_type);
905 : 0 : return false;
906 : : }
907 : : }
908 : :
909 : 9 : static inline size_t basic_type_element_size(GITypeTag element_tag) {
910 : 9 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag));
911 : :
912 [ + - - - : 9 : switch (element_tag) {
- + - - +
- - - + +
- ]
913 : 1 : case GI_TYPE_TAG_BOOLEAN:
914 : 1 : return sizeof(gboolean);
915 : 0 : case GI_TYPE_TAG_INT8:
916 : 0 : return sizeof(int8_t);
917 : 0 : case GI_TYPE_TAG_UINT8:
918 : 0 : return sizeof(uint8_t);
919 : 0 : case GI_TYPE_TAG_INT16:
920 : 0 : return sizeof(int16_t);
921 : 0 : case GI_TYPE_TAG_UINT16:
922 : 0 : return sizeof(uint16_t);
923 : 2 : case GI_TYPE_TAG_INT32:
924 : 2 : return sizeof(int32_t);
925 : 0 : case GI_TYPE_TAG_UINT32:
926 : 0 : return sizeof(uint32_t);
927 : 0 : case GI_TYPE_TAG_INT64:
928 : 0 : return sizeof(int64_t);
929 : 1 : case GI_TYPE_TAG_UINT64:
930 : 1 : return sizeof(uint64_t);
931 : 0 : case GI_TYPE_TAG_FLOAT:
932 : 0 : return sizeof(float);
933 : 0 : case GI_TYPE_TAG_DOUBLE:
934 : 0 : return sizeof(double);
935 : 0 : case GI_TYPE_TAG_GTYPE:
936 : 0 : return sizeof(GType);
937 : 2 : case GI_TYPE_TAG_UNICHAR:
938 : 2 : return sizeof(char32_t);
939 : 3 : case GI_TYPE_TAG_UTF8:
940 : : case GI_TYPE_TAG_FILENAME:
941 : 3 : return sizeof(char*);
942 : 0 : default:
943 : : g_return_val_if_reached(0);
944 : : }
945 : : }
946 : :
947 : 43 : size_t gjs_type_get_element_size(GITypeTag element_type,
948 : : GITypeInfo* type_info) {
949 [ + + + + : 43 : if (g_type_info_is_pointer(type_info) &&
+ + ]
950 : : element_type != GI_TYPE_TAG_ARRAY)
951 : 4 : return sizeof(void*);
952 : :
953 [ - - - + : 39 : switch (element_type) {
- + - + ]
954 : 0 : case GI_TYPE_TAG_GLIST:
955 : 0 : return sizeof(GSList);
956 : 0 : case GI_TYPE_TAG_GSLIST:
957 : 0 : return sizeof(GList);
958 : 0 : case GI_TYPE_TAG_ERROR:
959 : 0 : return sizeof(GError);
960 : 23 : case GI_TYPE_TAG_INTERFACE: {
961 : 23 : GI::AutoBaseInfo interface_info{g_type_info_get_interface(type_info)};
962 : :
963 [ + - - + : 23 : if (GI_IS_ENUM_INFO(interface_info))
- + ]
964 : 0 : return sizeof(unsigned); // enum and flags
965 [ + + ]: 23 : if (GI_IS_STRUCT_INFO(interface_info))
966 : 22 : return g_struct_info_get_size(interface_info);
967 [ - + ]: 1 : if (GI_IS_UNION_INFO(interface_info))
968 : 0 : return g_union_info_get_size(interface_info);
969 : 1 : return 0;
970 : 23 : }
971 : :
972 : 0 : case GI_TYPE_TAG_GHASH:
973 : 0 : return sizeof(void*);
974 : :
975 : 15 : case GI_TYPE_TAG_ARRAY:
976 [ + - ]: 15 : if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
977 : 15 : int length = g_type_info_get_array_length(type_info);
978 [ + - ]: 15 : if (length < 0)
979 : 15 : return sizeof(void*);
980 : :
981 : : GI::AutoTypeInfo param_info{
982 : 0 : g_type_info_get_param_type(type_info, 0)};
983 : 0 : GITypeTag param_tag = g_type_info_get_tag(param_info);
984 : 0 : return gjs_type_get_element_size(param_tag, param_info);
985 : 0 : }
986 : :
987 : 0 : return sizeof(void*);
988 : :
989 : 0 : case GI_TYPE_TAG_VOID:
990 : 0 : break;
991 : :
992 : 1 : default:
993 : 1 : return basic_type_element_size(element_type);
994 : : }
995 : :
996 : : g_return_val_if_reached(0);
997 : : }
998 : :
999 : 0 : static GArray* garray_new_for_storage_type(unsigned length,
1000 : : GITypeTag storage_type,
1001 : : GITypeInfo* type_info) {
1002 : 0 : size_t element_size = gjs_type_get_element_size(storage_type, type_info);
1003 : 0 : return g_array_sized_new(true, false, element_size, length);
1004 : : }
1005 : :
1006 : 8 : static GArray* garray_new_for_basic_type(unsigned length, GITypeTag tag) {
1007 : 8 : size_t element_size = basic_type_element_size(tag);
1008 : 8 : return g_array_sized_new(true, false, element_size, length);
1009 : : }
1010 : :
1011 : 12 : char* gjs_argument_display_name(const char* arg_name,
1012 : : GjsArgumentType arg_type) {
1013 [ + + - - : 12 : switch (arg_type) {
- - - ]
1014 : 10 : case GJS_ARGUMENT_ARGUMENT:
1015 : 10 : return g_strdup_printf("Argument '%s'", arg_name);
1016 : 2 : case GJS_ARGUMENT_RETURN_VALUE:
1017 : 2 : return g_strdup("Return value");
1018 : 0 : case GJS_ARGUMENT_FIELD:
1019 : 0 : return g_strdup_printf("Field '%s'", arg_name);
1020 : 0 : case GJS_ARGUMENT_LIST_ELEMENT:
1021 : 0 : return g_strdup("List element");
1022 : 0 : case GJS_ARGUMENT_HASH_ELEMENT:
1023 : 0 : return g_strdup("Hash element");
1024 : 0 : case GJS_ARGUMENT_ARRAY_ELEMENT:
1025 : 0 : return g_strdup("Array element");
1026 : 0 : default:
1027 : : g_assert_not_reached ();
1028 : : }
1029 : : }
1030 : :
1031 : 0 : [[nodiscard]] static const char* type_tag_to_human_string(
1032 : : GITypeInfo* type_info) {
1033 : : GITypeTag tag;
1034 : :
1035 : 0 : tag = g_type_info_get_tag(type_info);
1036 : :
1037 [ # # ]: 0 : if (tag == GI_TYPE_TAG_INTERFACE) {
1038 : 0 : GI::AutoBaseInfo interface{g_type_info_get_interface(type_info)};
1039 : 0 : return g_info_type_to_string(interface.type());
1040 : 0 : } else {
1041 : 0 : return g_type_tag_to_string(tag);
1042 : : }
1043 : : }
1044 : :
1045 : : static void
1046 : 0 : throw_invalid_argument(JSContext *context,
1047 : : JS::HandleValue value,
1048 : : GITypeInfo *arginfo,
1049 : : const char *arg_name,
1050 : : GjsArgumentType arg_type)
1051 : : {
1052 : 0 : Gjs::AutoChar display_name{gjs_argument_display_name(arg_name, arg_type)};
1053 : :
1054 : 0 : gjs_throw(context, "Expected type %s for %s but got type '%s'",
1055 : : type_tag_to_human_string(arginfo), display_name.get(),
1056 : : JS::InformalValueTypeName(value));
1057 : 0 : }
1058 : :
1059 : : GJS_JSAPI_RETURN_CONVENTION
1060 : 5 : static bool throw_invalid_argument_tag(JSContext* cx, JS::HandleValue value,
1061 : : GITypeTag type_tag, const char* arg_name,
1062 : : GjsArgumentType arg_type) {
1063 : 5 : Gjs::AutoChar display_name{gjs_argument_display_name(arg_name, arg_type)};
1064 : :
1065 : 5 : gjs_throw(cx, "Expected type %s for %s but got type '%s'",
1066 : : g_type_tag_to_string(type_tag), display_name.get(),
1067 : : JS::InformalValueTypeName(value));
1068 : 10 : return false;
1069 : 5 : }
1070 : :
1071 : : GJS_JSAPI_RETURN_CONVENTION
1072 : 4 : static bool throw_invalid_interface_argument(JSContext* cx,
1073 : : JS::HandleValue value,
1074 : : GIBaseInfo* interface_info,
1075 : : const char* arg_name,
1076 : : GjsArgumentType arg_type) {
1077 : 4 : Gjs::AutoChar display_name{gjs_argument_display_name(arg_name, arg_type)};
1078 : :
1079 : 4 : gjs_throw(cx, "Expected type %s for %s but got type '%s'",
1080 : : g_info_type_to_string(g_base_info_get_type(interface_info)),
1081 : : display_name.get(), JS::InformalValueTypeName(value));
1082 : 8 : return false;
1083 : 4 : }
1084 : :
1085 : 214 : bool gjs_array_to_basic_explicit_array(
1086 : : JSContext* cx, JS::HandleValue value, GITypeTag element_tag,
1087 : : const char* arg_name, GjsArgumentType arg_type, GjsArgumentFlags flags,
1088 : : void** contents_out, size_t* length_out) {
1089 : 214 : g_assert(contents_out && length_out && "forgot out parameter");
1090 : :
1091 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
1092 : : "Converting argument '%s' JS value %s to C array",
1093 : : arg_name, gjs_debug_value(value).c_str());
1094 : :
1095 [ + + + - : 428 : if ((value.isNull() && !(flags & GjsArgumentFlags::MAY_BE_NULL)) ||
+ + ]
1096 [ + + + + ]: 214 : (!value.isString() && !value.isObjectOrNull())) {
1097 : 4 : return throw_invalid_argument_tag(cx, value, element_tag, arg_name,
1098 : 4 : arg_type);
1099 : : }
1100 : :
1101 [ + + ]: 210 : if (value.isNull()) {
1102 : 11 : *contents_out = nullptr;
1103 : 11 : *length_out = 0;
1104 : 11 : return true;
1105 : : }
1106 : :
1107 [ + + ]: 199 : if (value.isString()) {
1108 : : // Allow strings as int8/uint8/int16/uint16 arrays
1109 : 18 : JS::RootedString str{cx, value.toString()};
1110 : 18 : return gjs_string_to_intarray(cx, str, element_tag, contents_out,
1111 : 18 : length_out);
1112 : 18 : }
1113 : :
1114 : 181 : JS::RootedObject array_obj{cx, &value.toObject()};
1115 [ + + + - : 199 : if (JS_IsUint8Array(array_obj) &&
+ + ]
1116 [ + - ]: 18 : (element_tag == GI_TYPE_TAG_INT8 || element_tag == GI_TYPE_TAG_UINT8)) {
1117 : 18 : GBytes* bytes = gjs_byte_array_get_bytes(array_obj);
1118 : 18 : *contents_out = g_bytes_unref_to_data(bytes, length_out);
1119 : 18 : return true;
1120 : : }
1121 : :
1122 : 163 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
1123 : : bool found_length;
1124 [ - + ]: 163 : if (!JS_HasPropertyById(cx, array_obj, atoms.length(), &found_length))
1125 : 0 : return false;
1126 [ + + ]: 163 : if (found_length) {
1127 : : uint32_t length;
1128 : :
1129 [ - + ]: 162 : if (!gjs_object_require_converted_property(cx, array_obj, nullptr,
1130 : : atoms.length(), &length)) {
1131 : 0 : return false;
1132 : : }
1133 : :
1134 : : // For basic types, type tag == storage type
1135 [ + + ]: 162 : if (!gjs_array_to_basic_array(cx, value, length, element_tag,
1136 : : contents_out))
1137 : 3 : return false;
1138 : :
1139 : 159 : *length_out = length;
1140 : 159 : return true;
1141 : : }
1142 : :
1143 : 1 : return throw_invalid_argument_tag(cx, value, element_tag, arg_name,
1144 : 1 : arg_type);
1145 : 181 : }
1146 : :
1147 : 258 : bool gjs_array_to_explicit_array(JSContext* context, JS::HandleValue value,
1148 : : GITypeInfo* type_info, const char* arg_name,
1149 : : GjsArgumentType arg_type, GITransfer transfer,
1150 : : GjsArgumentFlags flags, void** contents,
1151 : : size_t* length_p) {
1152 : 258 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
1153 : 258 : GITypeTag element_tag = g_type_info_get_tag(param_info);
1154 : :
1155 [ + - - + ]: 258 : if (GI_TYPE_TAG_IS_BASIC(element_tag)) {
1156 : 0 : return gjs_array_to_basic_explicit_array(context, value, element_tag,
1157 : : arg_name, arg_type, flags,
1158 : 0 : contents, length_p);
1159 : : }
1160 : :
1161 : : bool found_length;
1162 : :
1163 : : gjs_debug_marshal(
1164 : : GJS_DEBUG_GFUNCTION,
1165 : : "Converting argument '%s' JS value %s to C array, transfer %d",
1166 : : arg_name, gjs_debug_value(value).c_str(), transfer);
1167 : :
1168 [ + + + - : 516 : if ((value.isNull() && !(flags & GjsArgumentFlags::MAY_BE_NULL)) ||
- + ]
1169 [ + - - + ]: 258 : (!value.isString() && !value.isObjectOrNull())) {
1170 : 0 : throw_invalid_argument(context, value, param_info, arg_name, arg_type);
1171 : 0 : return false;
1172 : : }
1173 : :
1174 [ + + ]: 258 : if (value.isNull()) {
1175 : 1 : *contents = NULL;
1176 : 1 : *length_p = 0;
1177 [ - + ]: 257 : } else if (value.isString()) {
1178 : : /* Allow strings as int8/uint8/int16/uint16 arrays */
1179 : 0 : JS::RootedString str(context, value.toString());
1180 [ # # ]: 0 : if (!gjs_string_to_intarray(context, str, element_tag, contents, length_p))
1181 : 0 : return false;
1182 [ # # ]: 0 : } else {
1183 : 257 : JS::RootedObject array_obj(context, &value.toObject());
1184 [ - + - - : 257 : if (JS_IsUint8Array(array_obj) && (element_tag == GI_TYPE_TAG_INT8 ||
- - - + ]
1185 : : element_tag == GI_TYPE_TAG_UINT8)) {
1186 : 0 : GBytes* bytes = gjs_byte_array_get_bytes(array_obj);
1187 : 0 : *contents = g_bytes_unref_to_data(bytes, length_p);
1188 : 0 : return true;
1189 : : }
1190 : :
1191 : 257 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
1192 [ - + ]: 257 : if (!JS_HasPropertyById(context, array_obj, atoms.length(),
1193 : : &found_length))
1194 : 0 : return false;
1195 [ + - ]: 257 : if (found_length) {
1196 : : guint32 length;
1197 : :
1198 [ - + ]: 257 : if (!gjs_object_require_converted_property(
1199 : : context, array_obj, nullptr, atoms.length(), &length)) {
1200 : 1 : return false;
1201 : : } else {
1202 [ + + ]: 257 : if (!gjs_array_to_array(context,
1203 : : value,
1204 : : length,
1205 : : transfer,
1206 : : param_info,
1207 : : contents))
1208 : 1 : return false;
1209 : :
1210 : 256 : *length_p = length;
1211 : : }
1212 : : } else {
1213 : 0 : throw_invalid_argument(context, value, param_info, arg_name, arg_type);
1214 : 0 : return false;
1215 : : }
1216 [ + + ]: 257 : }
1217 : :
1218 : 257 : return true;
1219 : 258 : }
1220 : :
1221 : : namespace arg {
1222 : 20297 : [[nodiscard]] static bool is_gdk_atom(GIBaseInfo* info) {
1223 [ + + ]: 20298 : return (strcmp("Atom", g_base_info_get_name(info)) == 0 &&
1224 [ + - ]: 20298 : strcmp("Gdk", g_base_info_get_namespace(info)) == 0);
1225 : : }
1226 : : } // namespace arg
1227 : :
1228 : 5 : static void intern_gdk_atom(const char* name, GIArgument* ret) {
1229 : : GI::AutoFunctionInfo atom_intern_fun{
1230 : 5 : g_irepository_find_by_name(nullptr, "Gdk", "atom_intern")};
1231 : :
1232 : : GIArgument atom_intern_args[2];
1233 : :
1234 : : /* Can only store char * in GIArgument. First argument to gdk_atom_intern
1235 : : * is const char *, string isn't modified. */
1236 : 5 : gjs_arg_set(&atom_intern_args[0], name);
1237 : 5 : gjs_arg_set(&atom_intern_args[1], false);
1238 : :
1239 : 5 : mozilla::Unused << g_function_info_invoke(atom_intern_fun, atom_intern_args,
1240 : : 2, nullptr, 0, ret, nullptr);
1241 : 5 : }
1242 : :
1243 : 8 : static bool value_to_gdk_atom_gi_argument_internal(JSContext* cx,
1244 : : JS::HandleValue value,
1245 : : GIArgument* arg,
1246 : : const char* arg_name,
1247 : : GjsArgumentType arg_type) {
1248 [ + + + + : 8 : if (!value.isNull() && !value.isString()) {
+ + ]
1249 : : Gjs::AutoChar display_name{
1250 : 3 : gjs_argument_display_name(arg_name, arg_type)};
1251 : 3 : gjs_throw(cx, "Expected type String or null for %s but got type '%s'",
1252 : : display_name.get(), JS::InformalValueTypeName(value));
1253 : 3 : return false;
1254 : 3 : }
1255 : :
1256 [ + + ]: 5 : if (value.isNull()) {
1257 : 1 : intern_gdk_atom("NONE", arg);
1258 : 1 : return true;
1259 : : }
1260 : :
1261 : 4 : JS::RootedString str{cx, value.toString()};
1262 : 4 : JS::UniqueChars name{JS_EncodeStringToUTF8(cx, str)};
1263 [ - + ]: 4 : if (!name)
1264 : 0 : return false;
1265 : :
1266 : 4 : intern_gdk_atom(name.get(), arg);
1267 : 4 : return true;
1268 : 4 : }
1269 : :
1270 : : GJS_JSAPI_RETURN_CONVENTION
1271 : 729 : bool value_to_interface_gi_argument_internal(
1272 : : JSContext* cx, JS::HandleValue value, GIBaseInfo* interface_info,
1273 : : GITransfer transfer, GIArgument* arg, const char* arg_name,
1274 : : GjsArgumentType arg_type, GjsArgumentFlags flags) {
1275 : : GType gtype;
1276 : :
1277 [ - + ]: 729 : if (arg::is_gdk_atom(interface_info)) {
1278 : 0 : return value_to_gdk_atom_gi_argument_internal(cx, value, arg, arg_name,
1279 : 0 : arg_type);
1280 : : }
1281 : :
1282 [ + - + + : 729 : if (GI_IS_REGISTERED_TYPE_INFO(interface_info))
+ + + - +
+ - + - -
- - + - ]
1283 : 729 : gtype = g_registered_type_info_get_g_type(interface_info);
1284 : : else
1285 : 0 : gtype = G_TYPE_NONE;
1286 : :
1287 : : if (gtype != G_TYPE_NONE)
1288 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s",
1289 : : g_type_name(gtype));
1290 : :
1291 [ + + ]: 729 : if (gtype == G_TYPE_VALUE) {
1292 [ + + ]: 11 : if (flags & GjsArgumentFlags::CALLER_ALLOCATES) {
1293 [ - + ]: 3 : if (!gjs_value_to_g_value_no_copy(cx, value,
1294 : : gjs_arg_get<GValue*>(arg)))
1295 : 0 : return false;
1296 : :
1297 : 3 : return true;
1298 : : }
1299 : :
1300 : 8 : Gjs::AutoGValue gvalue;
1301 [ - + ]: 8 : if (!gjs_value_to_g_value(cx, value, &gvalue)) {
1302 : 0 : gjs_arg_unset(arg);
1303 : 0 : return false;
1304 : : }
1305 : :
1306 : 8 : gjs_arg_set(arg, g_boxed_copy(G_TYPE_VALUE, &gvalue));
1307 : 8 : return true;
1308 : :
1309 [ + + + + : 726 : } else if (GI_IS_ENUM_INFO(interface_info) == value.isObjectOrNull()) {
- + ]
1310 : : // for enum/flags, object/null are invalid. for everything else,
1311 : : // everything except object/null is invalid.
1312 : 0 : return throw_invalid_interface_argument(cx, value, interface_info,
1313 : 0 : arg_name, arg_type);
1314 : :
1315 [ - + ]: 718 : } else if (value.isNull()) {
1316 : 0 : gjs_arg_set(arg, nullptr);
1317 : 0 : return true;
1318 : :
1319 [ + + ]: 718 : } else if (value.isObject()) {
1320 : 598 : JS::RootedObject obj(cx, &value.toObject());
1321 [ + + - + : 1058 : if (GI_IS_STRUCT_INFO(interface_info) &&
- + ]
1322 : 460 : g_struct_info_is_gtype_struct(interface_info)) {
1323 : : GType actual_gtype;
1324 [ # # ]: 0 : if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
1325 : 0 : return false;
1326 : :
1327 [ # # ]: 0 : if (actual_gtype == G_TYPE_NONE) {
1328 : 0 : return throw_invalid_interface_argument(
1329 : 0 : cx, value, interface_info, arg_name, arg_type);
1330 : : }
1331 : :
1332 : : // We use peek here to simplify reference counting (we just ignore
1333 : : // transfer annotation, as GType classes are never really freed)
1334 : : // We know that the GType class is referenced at least once when
1335 : : // the JS constructor is initialized.
1336 : : void* klass;
1337 [ # # # # : 0 : if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE))
# # ]
1338 : 0 : klass = g_type_default_interface_peek(actual_gtype);
1339 : : else
1340 : 0 : klass = g_type_class_peek(actual_gtype);
1341 : :
1342 : 0 : gjs_arg_set(arg, klass);
1343 : 0 : return true;
1344 : : }
1345 : :
1346 : 598 : GType arg_gtype = gtype;
1347 [ + + + + : 611 : if (GI_IS_STRUCT_INFO(interface_info) && gtype == G_TYPE_NONE &&
+ - + + ]
1348 : 13 : !g_struct_info_is_foreign(interface_info)) {
1349 : 13 : GType actual_gtype = G_TYPE_NONE;
1350 : : // In case we have no known type from gi we should try to be
1351 : : // more dynamic and try to get the type from JS, to handle the
1352 : : // case in which we're handling a gpointer such as GTypeInstance
1353 : : // FIXME(3v1n0): would be nice to know if GI would give this info
1354 [ - + ]: 13 : if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
1355 : 0 : return false;
1356 : :
1357 [ + + ]: 13 : if (G_TYPE_IS_INSTANTIATABLE(actual_gtype))
1358 : 4 : gtype = actual_gtype;
1359 : : }
1360 : :
1361 [ - + ]: 736 : if ((GI_IS_STRUCT_INFO(interface_info) ||
1362 [ + + + + ]: 1196 : g_base_info_get_type(interface_info) == GI_INFO_TYPE_BOXED) &&
1363 [ + + + - ]: 460 : !g_type_is_a(gtype, G_TYPE_CLOSURE)) {
1364 : : // Handle Struct/Union first since we don't necessarily need a GType
1365 : : // for them. We special case Closures later, so skip them here.
1366 [ + - - + : 418 : if (g_type_is_a(gtype, G_TYPE_BYTES) && JS_IsUint8Array(obj)) {
- - - + ]
1367 : 0 : gjs_arg_set(arg, gjs_byte_array_get_bytes(obj));
1368 : 0 : return true;
1369 : : }
1370 [ + - - + : 418 : if (g_type_is_a(gtype, G_TYPE_ERROR)) {
- + ]
1371 : 0 : return ErrorBase::transfer_to_gi_argument(
1372 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer);
1373 : : }
1374 [ + + + - ]: 13 : if (arg_gtype != G_TYPE_NONE || gtype == G_TYPE_NONE ||
1375 [ + - ]: 4 : g_type_is_a(gtype, G_TYPE_BOXED) ||
1376 [ + + + - : 435 : g_type_is_a(gtype, G_TYPE_VALUE) ||
+ - + - +
+ ]
1377 [ - + ]: 4 : g_type_is_a(gtype, G_TYPE_VARIANT)) {
1378 [ - + ]: 414 : if (!BoxedBase::typecheck(cx, obj, interface_info)) {
1379 : 0 : gjs_arg_unset(arg);
1380 : 0 : return false;
1381 : : }
1382 : 828 : return BoxedBase::transfer_to_gi_argument(
1383 : 414 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1384 : : }
1385 : : }
1386 : :
1387 [ - + ]: 184 : if (GI_IS_UNION_INFO(interface_info)) {
1388 [ # # ]: 0 : if (!UnionBase::typecheck(cx, obj, interface_info)) {
1389 : 0 : gjs_arg_unset(arg);
1390 : 0 : return false;
1391 : : }
1392 : 0 : return UnionBase::transfer_to_gi_argument(
1393 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1394 : : }
1395 : :
1396 [ + - ]: 184 : if (gtype != G_TYPE_NONE) {
1397 [ + + + + : 184 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
1398 : 38 : return ObjectBase::transfer_to_gi_argument(
1399 : 19 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1400 : :
1401 [ + + - + : 165 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
1402 [ - + ]: 119 : if (!gjs_typecheck_param(cx, obj, gtype, true)) {
1403 : 0 : gjs_arg_unset(arg);
1404 : 0 : return false;
1405 : : }
1406 : 119 : gjs_arg_set(arg, gjs_g_param_from_param(cx, obj));
1407 [ - + ]: 119 : if (transfer != GI_TRANSFER_NOTHING)
1408 : 0 : g_param_spec_ref(gjs_arg_get<GParamSpec*>(arg));
1409 : 119 : return true;
1410 : :
1411 [ + - + + : 46 : } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + ]
1412 [ - + - - : 42 : if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
+ - ]
1413 : 42 : if (BoxedBase::typecheck(cx, obj, interface_info,
1414 [ + - + - ]: 84 : GjsTypecheckNoThrow{}) &&
1415 [ + - ]: 84 : BoxedBase::typecheck(cx, obj, gtype,
1416 : : GjsTypecheckNoThrow{})) {
1417 : 84 : return BoxedBase::transfer_to_gi_argument(
1418 : 42 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1419 : : }
1420 : :
1421 : : GClosure* closure =
1422 : 0 : Gjs::Closure::create_marshaled(cx, obj, "boxed");
1423 : : // GI doesn't know about floating GClosure references. We
1424 : : // guess that if this is a return value going from JS::Value
1425 : : // to GIArgument, it's intended to be passed to a C API that
1426 : : // will consume the floating reference.
1427 [ # # ]: 0 : if (arg_type != GJS_ARGUMENT_RETURN_VALUE) {
1428 : 0 : g_closure_ref(closure);
1429 : 0 : g_closure_sink(closure);
1430 : : }
1431 : 0 : gjs_arg_set(arg, closure);
1432 : 0 : return true;
1433 : : }
1434 : :
1435 : : // Should have been caught above as STRUCT/BOXED/UNION
1436 : 0 : gjs_throw(
1437 : : cx,
1438 : : "Boxed type %s registered for unexpected interface_type %s",
1439 : : g_type_name(gtype),
1440 : : g_info_type_to_string(
1441 : : g_base_info_get_type(interface_info)));
1442 : 0 : return false;
1443 : :
1444 [ + - ]: 4 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
1445 : 8 : return FundamentalBase::transfer_to_gi_argument(
1446 : 4 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1447 : :
1448 [ # # ]: 0 : } else if (G_TYPE_IS_INTERFACE(gtype)) {
1449 : : // Could be a GObject interface that's missing a prerequisite,
1450 : : // or could be a fundamental
1451 [ # # ]: 0 : if (ObjectBase::typecheck(cx, obj, gtype,
1452 : : GjsTypecheckNoThrow{})) {
1453 : 0 : return ObjectBase::transfer_to_gi_argument(
1454 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1455 : : }
1456 : :
1457 : : // If this typecheck fails, then it's neither an object nor a
1458 : : // fundamental
1459 : 0 : return FundamentalBase::transfer_to_gi_argument(
1460 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1461 : : }
1462 : :
1463 : 0 : gjs_throw(cx, "Unhandled GType %s unpacking GIArgument from Object",
1464 : : g_type_name(gtype));
1465 : 0 : gjs_arg_unset(arg);
1466 : 0 : return false;
1467 : : }
1468 : :
1469 : 0 : gjs_debug(GJS_DEBUG_GFUNCTION,
1470 : : "conversion of JSObject value %s to type %s failed",
1471 : 0 : gjs_debug_value(value).c_str(),
1472 : : g_base_info_get_name(interface_info));
1473 : :
1474 : 0 : gjs_throw(cx,
1475 : : "Unexpected unregistered type unpacking GIArgument from "
1476 : : "Object");
1477 : 0 : return false;
1478 : :
1479 [ + + ]: 718 : } else if (value.isNumber()) {
1480 [ + + + - : 116 : if (GI_IS_ENUM_INFO(interface_info)) {
+ - ]
1481 : : int64_t value_int64;
1482 : :
1483 [ - + ]: 116 : if (!JS::ToInt64(cx, value, &value_int64))
1484 : 0 : return false;
1485 : :
1486 [ + + ]: 116 : if (g_base_info_get_type(interface_info) == GI_INFO_TYPE_FLAGS) {
1487 [ - + ]: 10 : if (!_gjs_flags_value_is_valid(cx, gtype, value_int64))
1488 : 0 : return false;
1489 : : } else {
1490 [ - + ]: 106 : if (!_gjs_enum_value_is_valid(cx, interface_info, value_int64))
1491 : 0 : return false;
1492 : : }
1493 : :
1494 : 116 : gjs_arg_set<Gjs::Tag::Enum>(arg, _gjs_enum_to_int(value_int64));
1495 : 116 : return true;
1496 : :
1497 [ # # ]: 0 : } else if (gtype == G_TYPE_NONE) {
1498 : 0 : gjs_throw(cx,
1499 : : "Unexpected unregistered type unpacking GIArgument from "
1500 : : "Number");
1501 : 0 : return false;
1502 : : }
1503 : :
1504 : 0 : gjs_throw(cx, "Unhandled GType %s unpacking GIArgument from Number",
1505 : : g_type_name(gtype));
1506 : 0 : return false;
1507 : : }
1508 : :
1509 : 4 : gjs_debug(GJS_DEBUG_GFUNCTION,
1510 : : "JSObject type '%s' is neither null nor an object",
1511 : : JS::InformalValueTypeName(value));
1512 : 4 : return throw_invalid_interface_argument(cx, value, interface_info, arg_name,
1513 : 4 : arg_type);
1514 : : }
1515 : :
1516 : : template <typename TAG>
1517 : 303 : GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
1518 : : JSContext* cx, JS::HandleValue value, GIArgument* arg, const char* arg_name,
1519 : : GjsArgumentType arg_type) {
1520 : 303 : bool out_of_range = false;
1521 : :
1522 [ - + ]: 303 : if (!gjs_arg_set_from_js_value<TAG>(cx, value, arg, &out_of_range)) {
1523 [ # # ]: 0 : if (out_of_range) {
1524 : 0 : Gjs::AutoChar display_name{
1525 : : gjs_argument_display_name(arg_name, arg_type)};
1526 : 0 : gjs_throw(cx, "value %s is out of range for %s (type %s)",
1527 : 0 : std::to_string(gjs_arg_get<TAG>(arg)).c_str(),
1528 : : display_name.get(), Gjs::static_type_name<TAG>());
1529 : 0 : }
1530 : :
1531 : 0 : return false;
1532 : : }
1533 : :
1534 : : gjs_debug_marshal(
1535 : : GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
1536 : : Gjs::AutoChar{gjs_argument_display_name(arg_name, arg_type)}.get(),
1537 : : std::to_string(gjs_arg_get<TAG>(arg)).c_str(),
1538 : : Gjs::static_type_name<TAG>());
1539 : :
1540 : 303 : return true;
1541 : : }
1542 : :
1543 : 666 : static bool check_nullable_argument(JSContext* cx, const char* arg_name,
1544 : : GjsArgumentType arg_type,
1545 : : GITypeTag type_tag, GjsArgumentFlags flags,
1546 : : GIArgument* arg) {
1547 [ + + - + : 666 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL) && !gjs_arg_get<void*>(arg)) {
- + ]
1548 : : Gjs::AutoChar display_name{
1549 : 0 : gjs_argument_display_name(arg_name, arg_type)};
1550 : 0 : gjs_throw(cx, "%s (type %s) may not be null", display_name.get(),
1551 : : g_type_tag_to_string(type_tag));
1552 : 0 : return false;
1553 : 0 : }
1554 : :
1555 : 666 : return true;
1556 : : }
1557 : :
1558 : 446 : bool gjs_value_to_basic_gi_argument(JSContext* cx, JS::HandleValue value,
1559 : : GITypeTag type_tag, GIArgument* arg,
1560 : : const char* arg_name,
1561 : : GjsArgumentType arg_type,
1562 : : GjsArgumentFlags flags) {
1563 : 446 : g_assert(GI_TYPE_TAG_IS_BASIC(type_tag) &&
1564 : : "use gjs_value_to_gi_argument() for non-basic types");
1565 : :
1566 : : gjs_debug_marshal(
1567 : : GJS_DEBUG_GFUNCTION,
1568 : : "Converting argument '%s' JS value %s to GIArgument type %s", arg_name,
1569 : : gjs_debug_value(value).c_str(), g_type_tag_to_string(type_tag));
1570 : :
1571 [ - + - - : 446 : switch (type_tag) {
- + + + +
+ + + - +
+ + - ]
1572 : 0 : case GI_TYPE_TAG_VOID:
1573 : : // don't know how to handle non-pointer VOID
1574 : 0 : return throw_invalid_argument_tag(cx, value, type_tag, arg_name,
1575 : 0 : arg_type);
1576 : 46 : case GI_TYPE_TAG_INT8:
1577 : 46 : return gjs_arg_set_from_js_value<int8_t>(cx, value, arg, arg_name,
1578 : 46 : arg_type);
1579 : 0 : case GI_TYPE_TAG_UINT8:
1580 : 0 : return gjs_arg_set_from_js_value<uint8_t>(cx, value, arg, arg_name,
1581 : 0 : arg_type);
1582 : 0 : case GI_TYPE_TAG_INT16:
1583 : 0 : return gjs_arg_set_from_js_value<int16_t>(cx, value, arg, arg_name,
1584 : 0 : arg_type);
1585 : :
1586 : 0 : case GI_TYPE_TAG_UINT16:
1587 : 0 : return gjs_arg_set_from_js_value<uint16_t>(cx, value, arg, arg_name,
1588 : 0 : arg_type);
1589 : :
1590 : 125 : case GI_TYPE_TAG_INT32:
1591 : 125 : return gjs_arg_set_from_js_value<int32_t>(cx, value, arg, arg_name,
1592 : 125 : arg_type);
1593 : :
1594 : 8 : case GI_TYPE_TAG_UINT32:
1595 : 8 : return gjs_arg_set_from_js_value<uint32_t>(cx, value, arg, arg_name,
1596 : 8 : arg_type);
1597 : :
1598 : 80 : case GI_TYPE_TAG_INT64:
1599 : 80 : return gjs_arg_set_from_js_value<int64_t>(cx, value, arg, arg_name,
1600 : 80 : arg_type);
1601 : :
1602 : 4 : case GI_TYPE_TAG_UINT64:
1603 : 4 : return gjs_arg_set_from_js_value<uint64_t>(cx, value, arg, arg_name,
1604 : 4 : arg_type);
1605 : :
1606 : 87 : case GI_TYPE_TAG_BOOLEAN:
1607 : 87 : gjs_arg_set(arg, JS::ToBoolean(value));
1608 : 87 : return true;
1609 : :
1610 : 13 : case GI_TYPE_TAG_FLOAT:
1611 : 13 : return gjs_arg_set_from_js_value<float>(cx, value, arg, arg_name,
1612 : 13 : arg_type);
1613 : :
1614 : 27 : case GI_TYPE_TAG_DOUBLE:
1615 : 27 : return gjs_arg_set_from_js_value<double>(cx, value, arg, arg_name,
1616 : 27 : arg_type);
1617 : :
1618 : 0 : case GI_TYPE_TAG_UNICHAR:
1619 [ # # ]: 0 : if (value.isString()) {
1620 : 0 : return gjs_unichar_from_string(cx, value,
1621 : 0 : &gjs_arg_member<char32_t>(arg));
1622 : : }
1623 : :
1624 : 0 : return throw_invalid_argument_tag(cx, value, type_tag, arg_name,
1625 : 0 : arg_type);
1626 : :
1627 : 3 : case GI_TYPE_TAG_GTYPE:
1628 [ + - ]: 3 : if (value.isObjectOrNull()) {
1629 : : GType gtype;
1630 : 3 : JS::RootedObject obj{cx, value.toObjectOrNull()};
1631 [ - + ]: 3 : if (!gjs_gtype_get_actual_gtype(cx, obj, >ype))
1632 : 0 : return false;
1633 : :
1634 [ - + ]: 3 : if (gtype == G_TYPE_INVALID)
1635 : 0 : return false;
1636 : 3 : gjs_arg_set<Gjs::Tag::GType>(arg, gtype);
1637 : 3 : return true;
1638 : 3 : }
1639 : :
1640 : 0 : return throw_invalid_argument_tag(cx, value, type_tag, arg_name,
1641 : 0 : arg_type);
1642 : :
1643 : 2 : case GI_TYPE_TAG_FILENAME:
1644 [ - + ]: 2 : if (value.isNull()) {
1645 : 0 : gjs_arg_set(arg, nullptr);
1646 [ + - ]: 2 : } else if (value.isString()) {
1647 : 2 : Gjs::AutoChar filename_str;
1648 [ - + ]: 2 : if (!gjs_string_to_filename(cx, value, &filename_str))
1649 : 0 : return false;
1650 : :
1651 : 2 : gjs_arg_set(arg, filename_str.release());
1652 [ + - ]: 2 : } else {
1653 : 0 : return throw_invalid_argument_tag(cx, value, type_tag, arg_name,
1654 : 0 : arg_type);
1655 : : }
1656 : :
1657 : 2 : return check_nullable_argument(cx, arg_name, arg_type, type_tag,
1658 : 2 : flags, arg);
1659 : :
1660 : 51 : case GI_TYPE_TAG_UTF8:
1661 [ - + ]: 51 : if (value.isNull()) {
1662 : 0 : gjs_arg_set(arg, nullptr);
1663 [ + - ]: 51 : } else if (value.isString()) {
1664 : 51 : JS::RootedString str{cx, value.toString()};
1665 : 51 : JS::UniqueChars utf8_str{JS_EncodeStringToUTF8(cx, str)};
1666 [ - + ]: 51 : if (!utf8_str)
1667 : 0 : return false;
1668 : :
1669 : 51 : gjs_arg_set(
1670 : 102 : arg, Gjs::js_chars_to_glib(std::move(utf8_str)).release());
1671 [ + - + - ]: 51 : } else {
1672 : 0 : return throw_invalid_argument_tag(cx, value, type_tag, arg_name,
1673 : 0 : arg_type);
1674 : : }
1675 : :
1676 : 51 : return check_nullable_argument(cx, arg_name, arg_type, type_tag,
1677 : 51 : flags, arg);
1678 : :
1679 : 0 : default:
1680 : : g_return_val_if_reached(false); // non-basic type
1681 : : }
1682 : : }
1683 : :
1684 : 4 : bool gjs_value_to_gerror_gi_argument(JSContext* cx, JS::HandleValue value,
1685 : : GITransfer transfer, GIArgument* arg,
1686 : : const char* arg_name,
1687 : : GjsArgumentType arg_type,
1688 : : GjsArgumentFlags flags) {
1689 : : gjs_debug_marshal(
1690 : : GJS_DEBUG_GFUNCTION,
1691 : : "Converting argument '%s' JS value %s to GIArgument type error",
1692 : : arg_name, gjs_debug_value(value).c_str());
1693 : :
1694 [ + + ]: 4 : if (value.isNull()) {
1695 : 1 : gjs_arg_set(arg, nullptr);
1696 [ + - ]: 3 : } else if (value.isObject()) {
1697 : 3 : JS::RootedObject obj(cx, &value.toObject());
1698 [ - + ]: 3 : if (!ErrorBase::transfer_to_gi_argument(cx, obj, arg, GI_DIRECTION_IN,
1699 : : transfer))
1700 : 0 : return false;
1701 [ + - ]: 3 : } else {
1702 : 0 : return throw_invalid_argument_tag(cx, value, GI_TYPE_TAG_ERROR,
1703 : 0 : arg_name, arg_type);
1704 : : }
1705 : :
1706 : 4 : return check_nullable_argument(cx, arg_name, arg_type, GI_TYPE_TAG_ERROR,
1707 : 4 : flags, arg);
1708 : : }
1709 : :
1710 : 8 : bool gjs_value_to_gdk_atom_gi_argument(JSContext* cx, JS::HandleValue value,
1711 : : GIArgument* arg, const char* arg_name,
1712 : : GjsArgumentType arg_type) {
1713 : : gjs_debug_marshal(
1714 : : GJS_DEBUG_GFUNCTION,
1715 : : "Converting argument '%s' JS value %s to GIArgument type interface",
1716 : : arg_name, gjs_debug_value(value).c_str());
1717 : :
1718 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is GdkAtom");
1719 : :
1720 : 8 : return value_to_gdk_atom_gi_argument_internal(cx, value, arg, arg_name,
1721 : 8 : arg_type);
1722 : : }
1723 : :
1724 : : // Convert a JS value to GIArgument, specifically for arguments with type tag
1725 : : // GI_TYPE_TAG_INTERFACE.
1726 : 729 : bool gjs_value_to_interface_gi_argument(JSContext* cx, JS::HandleValue value,
1727 : : GIBaseInfo* interface_info,
1728 : : GITransfer transfer, GIArgument* arg,
1729 : : const char* arg_name,
1730 : : GjsArgumentType arg_type,
1731 : : GjsArgumentFlags flags) {
1732 : 729 : g_assert(interface_info);
1733 : :
1734 [ - + ]: 729 : if (arg::is_gdk_atom(interface_info)) {
1735 : 0 : return gjs_value_to_gdk_atom_gi_argument(cx, value, arg, arg_name,
1736 : 0 : arg_type);
1737 : : }
1738 : :
1739 : : gjs_debug_marshal(
1740 : : GJS_DEBUG_GFUNCTION,
1741 : : "Converting argument '%s' JS value %s to GIArgument type interface",
1742 : : arg_name, gjs_debug_value(value).c_str());
1743 : :
1744 [ + + - + : 1200 : if (GI_IS_STRUCT_INFO(interface_info) &&
- + ]
1745 : 471 : g_struct_info_is_foreign(interface_info)) {
1746 : 0 : return gjs_struct_foreign_convert_to_gi_argument(
1747 : : cx, value, interface_info, arg_name, arg_type, transfer, flags,
1748 : 0 : arg);
1749 : : }
1750 : :
1751 [ + + ]: 729 : if (!value_to_interface_gi_argument_internal(cx, value, interface_info,
1752 : : transfer, arg, arg_name,
1753 : : arg_type, flags))
1754 : 4 : return false;
1755 : :
1756 [ + + + + : 725 : if (GI_IS_ENUM_INFO(interface_info))
+ + ]
1757 : 116 : return true;
1758 : :
1759 : 609 : return check_nullable_argument(cx, arg_name, arg_type,
1760 : 609 : GI_TYPE_TAG_INTERFACE, flags, arg);
1761 : : }
1762 : :
1763 : : template <typename T>
1764 : 16 : GJS_JSAPI_RETURN_CONVENTION static bool basic_array_to_linked_list(
1765 : : JSContext* cx, JS::HandleValue value, GITypeTag element_tag,
1766 : : const char* arg_name, GjsArgumentType arg_type, T** list_p) {
1767 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
1768 : 16 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag) &&
1769 : : "use gjs_array_to_g_list() for lists containing non-basic types");
1770 : :
1771 : 16 : constexpr GITypeTag list_tag =
1772 : : std::is_same_v<T, GList> ? GI_TYPE_TAG_GLIST : GI_TYPE_TAG_GSLIST;
1773 : :
1774 : : // While a list can be NULL in C, that means empty array in JavaScript, it
1775 : : // doesn't mean null in JavaScript.
1776 [ - + ]: 16 : if (!value.isObject())
1777 : 0 : return false;
1778 : :
1779 : 16 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
1780 : 16 : JS::RootedObject array_obj(cx, &value.toObject());
1781 : :
1782 : : bool found_length;
1783 [ - + ]: 16 : if (!JS_HasPropertyById(cx, array_obj, atoms.length(), &found_length))
1784 : 0 : return false;
1785 [ - + ]: 16 : if (!found_length) {
1786 : 0 : return throw_invalid_argument_tag(cx, value, list_tag, arg_name,
1787 : 0 : arg_type);
1788 : : }
1789 : :
1790 : : uint32_t length;
1791 [ - + ]: 16 : if (!gjs_object_require_converted_property(cx, array_obj, nullptr,
1792 : : atoms.length(), &length)) {
1793 : 0 : return throw_invalid_argument_tag(cx, value, list_tag, arg_name,
1794 : 0 : arg_type);
1795 : : }
1796 : :
1797 : 16 : JS::RootedObject array{cx, value.toObjectOrNull()};
1798 : 16 : JS::RootedValue elem{cx};
1799 : 16 : T* list = nullptr;
1800 : :
1801 [ + + ]: 58 : for (size_t i = 0; i < length; ++i) {
1802 : 42 : GIArgument elem_arg = {0};
1803 : :
1804 : 42 : elem = JS::UndefinedValue();
1805 [ - + ]: 42 : if (!JS_GetElement(cx, array, i, &elem)) {
1806 : 0 : gjs_throw(cx, "Missing array element %zu", i);
1807 : 0 : return false;
1808 : : }
1809 : :
1810 [ - + ]: 42 : if (!gjs_value_to_basic_gi_argument(cx, elem, element_tag, &elem_arg,
1811 : : arg_name, GJS_ARGUMENT_LIST_ELEMENT,
1812 : : GjsArgumentFlags::NONE)) {
1813 : 0 : return false;
1814 : : }
1815 : :
1816 : : void* hash_pointer =
1817 : 42 : gi_type_tag_hash_pointer_from_argument(element_tag, &elem_arg);
1818 : :
1819 : : if constexpr (std::is_same_v<T, GList>)
1820 : 23 : list = g_list_prepend(list, hash_pointer);
1821 : : else if constexpr (std::is_same_v<T, GSList>)
1822 : 19 : list = g_slist_prepend(list, hash_pointer);
1823 : : }
1824 : :
1825 : : if constexpr (std::is_same_v<T, GList>)
1826 : 8 : list = g_list_reverse(list);
1827 : : else if constexpr (std::is_same_v<T, GSList>)
1828 : 8 : list = g_slist_reverse(list);
1829 : :
1830 : 16 : *list_p = list;
1831 : :
1832 : 16 : return true;
1833 : 16 : }
1834 : :
1835 : 8 : bool gjs_value_to_basic_glist_gi_argument(JSContext* cx, JS::HandleValue value,
1836 : : GITypeTag element_tag,
1837 : : GIArgument* arg, const char* arg_name,
1838 : : GjsArgumentType arg_type) {
1839 : 8 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag) &&
1840 : : "use gjs_array_to_g_list() for lists containing non-basic types");
1841 : :
1842 : : gjs_debug_marshal(
1843 : : GJS_DEBUG_GFUNCTION,
1844 : : "Converting argument '%s' JS value %s to GIArgument type glist",
1845 : : arg_name, gjs_debug_value(value).c_str());
1846 : :
1847 : 8 : return basic_array_to_linked_list(cx, value, element_tag, arg_name,
1848 : 16 : arg_type, &gjs_arg_member<GList*>(arg));
1849 : : }
1850 : :
1851 : 8 : bool gjs_value_to_basic_gslist_gi_argument(JSContext* cx, JS::HandleValue value,
1852 : : GITypeTag element_tag,
1853 : : GIArgument* arg,
1854 : : const char* arg_name,
1855 : : GjsArgumentType arg_type) {
1856 : 8 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag) &&
1857 : : "use gjs_array_to_g_list() for lists containing non-basic types");
1858 : :
1859 : : gjs_debug_marshal(
1860 : : GJS_DEBUG_GFUNCTION,
1861 : : "Converting argument '%s' JS value %s to GIArgument type gslist",
1862 : : arg_name, gjs_debug_value(value).c_str());
1863 : :
1864 : 8 : return basic_array_to_linked_list(cx, value, element_tag, arg_name,
1865 : 16 : arg_type, &gjs_arg_member<GSList*>(arg));
1866 : : }
1867 : :
1868 : 13 : bool gjs_value_to_basic_ghash_gi_argument(
1869 : : JSContext* cx, JS::HandleValue value, GITypeTag key_tag,
1870 : : GITypeTag value_tag, GITransfer transfer, GIArgument* arg,
1871 : : const char* arg_name, GjsArgumentType arg_type, GjsArgumentFlags flags) {
1872 : 13 : g_assert(GI_TYPE_TAG_IS_BASIC(key_tag) &&
1873 : : "use gjs_object_to_g_hash() for hashes with non-basic key types");
1874 : 13 : g_assert(
1875 : : GI_TYPE_TAG_IS_BASIC(value_tag) &&
1876 : : "use gjs_object_to_g_hash() for hashes with non-basic value types");
1877 : :
1878 : : gjs_debug_marshal(
1879 : : GJS_DEBUG_GFUNCTION,
1880 : : "Converting argument '%s' JS value %s to GIArgument type ghash",
1881 : : arg_name, gjs_debug_value(value).c_str());
1882 : :
1883 [ + + ]: 13 : if (value.isNull()) {
1884 [ - + ]: 1 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) {
1885 : 0 : return throw_invalid_argument_tag(cx, value, GI_TYPE_TAG_GHASH,
1886 : 0 : arg_name, arg_type);
1887 : : }
1888 : 1 : gjs_arg_set(arg, nullptr);
1889 : 1 : return true;
1890 : : }
1891 : :
1892 [ - + ]: 12 : if (!value.isObject()) {
1893 : 0 : return throw_invalid_argument_tag(cx, value, GI_TYPE_TAG_GHASH,
1894 : 0 : arg_name, arg_type);
1895 : : }
1896 : :
1897 [ - + ]: 12 : if (transfer == GI_TRANSFER_CONTAINER) {
1898 [ # # # # : 0 : if (Gjs::basic_type_needs_release(key_tag) ||
# # ]
1899 : 0 : Gjs::basic_type_needs_release(value_tag)) {
1900 : : // See comment in gjs_value_to_g_hash()
1901 : 0 : gjs_throw(cx, "Container transfer for in parameters not supported");
1902 : 0 : return false;
1903 : : }
1904 : :
1905 : 0 : transfer = GI_TRANSFER_NOTHING;
1906 : : }
1907 : :
1908 : 12 : JS::RootedObject props{cx, &value.toObject()};
1909 : 12 : JS::Rooted<JS::IdVector> ids{cx, cx};
1910 [ - + ]: 12 : if (!JS_Enumerate(cx, props, &ids))
1911 : 0 : return false;
1912 : :
1913 : : Gjs::AutoPointer<GHashTable, GHashTable, g_hash_table_destroy> result{
1914 : 12 : create_hash_table_for_key_type(key_tag)};
1915 : :
1916 : 12 : JS::RootedValue v_key{cx}, v_val{cx};
1917 : 12 : JS::RootedId cur_id{cx};
1918 [ + + ]: 57 : for (size_t id_ix = 0, id_len = ids.length(); id_ix < id_len; ++id_ix) {
1919 : 45 : cur_id = ids[id_ix];
1920 : : void* key_ptr;
1921 : : void* val_ptr;
1922 : 45 : GIArgument val_arg = {0};
1923 : :
1924 : 45 : if (!JS_IdToValue(cx, cur_id, &v_key) ||
1925 : : // Type check key type.
1926 [ + - ]: 45 : !value_to_ghashtable_key(cx, v_key, key_tag, &key_ptr) ||
1927 [ + - + - ]: 135 : !JS_GetPropertyById(cx, props, cur_id, &v_val) ||
1928 : : // Type check and convert value to a C type
1929 [ - + - + ]: 90 : !gjs_value_to_basic_gi_argument(cx, v_val, value_tag, &val_arg,
1930 : : nullptr, GJS_ARGUMENT_HASH_ELEMENT,
1931 : : GjsArgumentFlags::MAY_BE_NULL))
1932 : 0 : return false;
1933 : :
1934 : : // Use heap-allocated values for types that don't fit in a pointer
1935 [ + + ]: 45 : if (value_tag == GI_TYPE_TAG_INT64) {
1936 : 4 : val_ptr = heap_value_new_from_arg<int64_t>(&val_arg);
1937 [ + + ]: 41 : } else if (value_tag == GI_TYPE_TAG_UINT64) {
1938 : 4 : val_ptr = heap_value_new_from_arg<uint64_t>(&val_arg);
1939 [ + + ]: 37 : } else if (value_tag == GI_TYPE_TAG_FLOAT) {
1940 : 4 : val_ptr = heap_value_new_from_arg<float>(&val_arg);
1941 [ + + ]: 33 : } else if (value_tag == GI_TYPE_TAG_DOUBLE) {
1942 : 4 : val_ptr = heap_value_new_from_arg<double>(&val_arg);
1943 : : } else {
1944 : : // Other types are simply stuffed inside the pointer
1945 : : val_ptr =
1946 : 29 : gi_type_tag_hash_pointer_from_argument(value_tag, &val_arg);
1947 : : }
1948 : :
1949 : 45 : g_hash_table_insert(result, key_ptr, val_ptr);
1950 : : }
1951 : :
1952 : 12 : gjs_arg_set(arg, result.release());
1953 : 12 : return true;
1954 : 12 : }
1955 : :
1956 : 94 : bool gjs_value_to_basic_array_gi_argument(JSContext* cx, JS::HandleValue value,
1957 : : GITypeTag element_tag,
1958 : : GIArrayType array_type,
1959 : : GIArgument* arg, const char* arg_name,
1960 : : GjsArgumentType arg_type,
1961 : : GjsArgumentFlags flags) {
1962 : 13 : Gjs::AutoPointer<void> data;
1963 : : size_t length;
1964 [ + + ]: 94 : if (!gjs_array_to_basic_explicit_array(cx, value, element_tag, arg_name,
1965 : : arg_type, flags, data.out(),
1966 : : &length)) {
1967 : 5 : return false;
1968 : : }
1969 : :
1970 [ + + ]: 89 : if (array_type == GI_ARRAY_TYPE_C) {
1971 : 78 : gjs_arg_set(arg, data.release());
1972 [ + + ]: 11 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
1973 : 8 : GArray* array = garray_new_for_basic_type(length, element_tag);
1974 : :
1975 [ + - ]: 8 : if (data)
1976 : 8 : g_array_append_vals(array, data, length);
1977 : 8 : gjs_arg_set(arg, array);
1978 [ - + ]: 3 : } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
1979 : 0 : return gjs_value_to_byte_array_gi_argument(cx, value, arg, arg_name,
1980 : 0 : flags);
1981 [ + - ]: 3 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
1982 : 3 : GPtrArray* array = g_ptr_array_sized_new(length);
1983 : :
1984 : 3 : g_ptr_array_set_size(array, length);
1985 [ + - ]: 3 : if (data)
1986 : 3 : memcpy(array->pdata, data, sizeof(void*) * length);
1987 : 3 : gjs_arg_set(arg, array);
1988 : : }
1989 : 89 : return true;
1990 : 94 : }
1991 : :
1992 : 4 : bool gjs_value_to_byte_array_gi_argument(JSContext* cx, JS::HandleValue value,
1993 : : GIArgument* arg, const char* arg_name,
1994 : : GjsArgumentFlags flags) {
1995 : : // First, let's handle the case where we're passed an instance of
1996 : : // Uint8Array and it needs to be marshalled to GByteArray.
1997 [ + - ]: 4 : if (value.isObject()) {
1998 : 4 : JSObject* bytearray_obj = &value.toObject();
1999 [ + + ]: 4 : if (JS_IsUint8Array(bytearray_obj)) {
2000 : 2 : gjs_arg_set(arg, gjs_byte_array_get_byte_array(bytearray_obj));
2001 : 2 : return true;
2002 : : }
2003 : : }
2004 : :
2005 : 2 : Gjs::AutoPointer<void> data;
2006 : : size_t length;
2007 [ - + ]: 2 : if (!gjs_array_to_basic_explicit_array(cx, value, GI_TYPE_TAG_UINT8,
2008 : : arg_name, GJS_ARGUMENT_ARGUMENT,
2009 : : flags, data.out(), &length)) {
2010 : 0 : return false;
2011 : : }
2012 : :
2013 : 2 : GByteArray* byte_array = g_byte_array_sized_new(length);
2014 : :
2015 [ + - ]: 2 : if (data)
2016 : 2 : g_byte_array_append(byte_array, data.as<const uint8_t>(), length);
2017 : 2 : gjs_arg_set(arg, byte_array);
2018 : 2 : return true;
2019 : 2 : }
2020 : :
2021 : 1014 : bool gjs_value_to_gi_argument(JSContext* context, JS::HandleValue value,
2022 : : GITypeInfo* type_info, const char* arg_name,
2023 : : GjsArgumentType arg_type, GITransfer transfer,
2024 : : GjsArgumentFlags flags, GIArgument* arg) {
2025 : 1014 : GITypeTag type_tag = g_type_info_get_tag(type_info);
2026 : 1014 : bool is_pointer = g_type_info_is_pointer(type_info);
2027 : :
2028 [ + + ]: 1014 : if (Gjs::is_basic_type(type_tag, is_pointer)) {
2029 : 353 : return gjs_value_to_basic_gi_argument(context, value, type_tag, arg,
2030 : 353 : arg_name, arg_type, flags);
2031 : : }
2032 : :
2033 [ - + ]: 661 : if (type_tag == GI_TYPE_TAG_ERROR) {
2034 : 0 : return gjs_value_to_gerror_gi_argument(context, value, transfer, arg,
2035 : 0 : arg_name, arg_type, flags);
2036 : : }
2037 : :
2038 [ + + ]: 661 : if (type_tag == GI_TYPE_TAG_INTERFACE) {
2039 : 602 : GI::AutoBaseInfo interface_info{g_type_info_get_interface(type_info)};
2040 : 602 : return gjs_value_to_interface_gi_argument(context, value,
2041 : : interface_info, transfer, arg,
2042 : 602 : arg_name, arg_type, flags);
2043 : 602 : }
2044 : :
2045 [ + - - + ]: 59 : if (type_tag == GI_TYPE_TAG_GLIST || type_tag == GI_TYPE_TAG_GSLIST) {
2046 : 0 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
2047 : 0 : GITypeTag element_tag = g_type_info_get_tag(element_type);
2048 : 0 : bool element_is_pointer = g_type_info_is_pointer(element_type);
2049 [ # # ]: 0 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
2050 [ # # ]: 0 : if (type_tag == GI_TYPE_TAG_GLIST)
2051 : 0 : return gjs_value_to_basic_glist_gi_argument(
2052 : 0 : context, value, element_tag, arg, arg_name, arg_type);
2053 : 0 : return gjs_value_to_basic_gslist_gi_argument(
2054 : 0 : context, value, element_tag, arg, arg_name, arg_type);
2055 : : }
2056 : : // else, fall through to generic marshaller
2057 : :
2058 [ - - + + ]: 59 : } else if (type_tag == GI_TYPE_TAG_GHASH) {
2059 : 1 : GI::AutoTypeInfo key_type{g_type_info_get_param_type(type_info, 0)};
2060 : 1 : GITypeTag key_tag = g_type_info_get_tag(key_type);
2061 : 1 : bool key_is_pointer = g_type_info_is_pointer(key_type);
2062 : :
2063 : 1 : GI::AutoTypeInfo value_type{g_type_info_get_param_type(type_info, 1)};
2064 : 1 : GITypeTag value_tag = g_type_info_get_tag(value_type);
2065 : 1 : bool value_is_pointer = g_type_info_is_pointer(value_type);
2066 : :
2067 [ + - - + : 2 : if (Gjs::is_basic_type(key_tag, key_is_pointer) &&
- + ]
2068 : 1 : Gjs::is_basic_type(value_tag, value_is_pointer)) {
2069 : 0 : return gjs_value_to_basic_ghash_gi_argument(
2070 : : context, value, key_tag, value_tag, transfer, arg, arg_name,
2071 : 0 : arg_type, flags);
2072 : : }
2073 : : // else, fall through to generic marshaller
2074 : :
2075 [ + - + - : 59 : } else if (type_tag == GI_TYPE_TAG_ARRAY) {
+ - ]
2076 : 58 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
2077 : 58 : GITypeTag element_tag = g_type_info_get_tag(element_type);
2078 : 58 : bool element_is_pointer = g_type_info_is_pointer(element_type);
2079 : 58 : GIArrayType array_type = g_type_info_get_array_type(type_info);
2080 : :
2081 [ + + ]: 58 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
2082 : 47 : return gjs_value_to_basic_array_gi_argument(
2083 : : context, value, element_tag, array_type, arg, arg_name,
2084 : 47 : arg_type, flags);
2085 : : }
2086 : : // else, fall through to generic marshaller
2087 [ + + ]: 58 : }
2088 : :
2089 : : gjs_debug_marshal(
2090 : : GJS_DEBUG_GFUNCTION,
2091 : : "Converting argument '%s' JS value %s to GIArgument type %s", arg_name,
2092 : : gjs_debug_value(value).c_str(), g_type_tag_to_string(type_tag));
2093 : :
2094 [ - - - + : 12 : switch (type_tag) {
+ - ]
2095 : 0 : case GI_TYPE_TAG_VOID:
2096 : 0 : g_assert(is_pointer &&
2097 : : "non-pointers should be handled by "
2098 : : "gjs_value_to_basic_gi_argument()");
2099 : : // void pointer; cannot marshal. Pass null to C if argument is nullable.
2100 : 0 : gjs_arg_unset(arg);
2101 : 0 : return check_nullable_argument(context, arg_name, arg_type, type_tag,
2102 : 0 : flags, arg);
2103 : 0 : case GI_TYPE_TAG_GLIST:
2104 : 0 : return gjs_array_to_g_list(context, value, type_info, transfer,
2105 : : arg_name, arg_type,
2106 : 0 : &gjs_arg_member<GList*>(arg));
2107 : 0 : case GI_TYPE_TAG_GSLIST:
2108 : 0 : return gjs_array_to_g_list(context, value, type_info, transfer,
2109 : : arg_name, arg_type,
2110 : 0 : &gjs_arg_member<GSList*>(arg));
2111 : :
2112 : 1 : case GI_TYPE_TAG_GHASH:
2113 [ - + ]: 1 : if (value.isNull()) {
2114 : 0 : gjs_arg_set(arg, nullptr);
2115 [ # # ]: 0 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) {
2116 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
2117 : : arg_type);
2118 : 0 : return false;
2119 : : }
2120 [ - + ]: 1 : } else if (!value.isObject()) {
2121 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
2122 : : arg_type);
2123 : 0 : return false;
2124 : : } else {
2125 : : GHashTable *ghash;
2126 : 1 : JS::RootedObject props(context, &value.toObject());
2127 [ - + ]: 1 : if (!gjs_object_to_g_hash(context, props, type_info, transfer,
2128 : : &ghash)) {
2129 : 0 : return false;
2130 : : }
2131 : :
2132 : 1 : gjs_arg_set(arg, ghash);
2133 [ + - ]: 1 : }
2134 : 1 : break;
2135 : :
2136 : 11 : case GI_TYPE_TAG_ARRAY: {
2137 : 11 : Gjs::AutoPointer<void> data;
2138 : : size_t length;
2139 : 11 : GIArrayType array_type = g_type_info_get_array_type(type_info);
2140 : :
2141 [ - + ]: 11 : if (!gjs_array_to_explicit_array(context, value, type_info, arg_name,
2142 : : arg_type, transfer, flags, data.out(),
2143 : : &length)) {
2144 : 0 : return false;
2145 : : }
2146 : :
2147 : 11 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
2148 [ + - ]: 11 : if (array_type == GI_ARRAY_TYPE_C) {
2149 : 11 : gjs_arg_set(arg, data.release());
2150 [ # # ]: 0 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
2151 : 0 : GITypeTag storage_type = g_type_info_get_storage_type(param_info);
2152 : : GArray* array =
2153 : 0 : garray_new_for_storage_type(length, storage_type, param_info);
2154 : :
2155 [ # # ]: 0 : if (data)
2156 : 0 : g_array_append_vals(array, data, length);
2157 : 0 : gjs_arg_set(arg, array);
2158 [ # # ]: 0 : } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
2159 : : // handled in gjs_value_to_basic_array_gi_argument()
2160 : : g_assert_not_reached();
2161 [ # # ]: 0 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
2162 : 0 : GPtrArray *array = g_ptr_array_sized_new(length);
2163 : :
2164 : 0 : g_ptr_array_set_size(array, length);
2165 [ # # ]: 0 : if (data)
2166 : 0 : memcpy(array->pdata, data, sizeof(void*) * length);
2167 : 0 : gjs_arg_set(arg, array);
2168 : : }
2169 : 11 : break;
2170 [ - + ]: 22 : }
2171 : 0 : default:
2172 : : // basic types handled in gjs_value_to_basic_gi_argument(), ERROR
2173 : : // handled in gjs_value_to_gerror_gi_argument(), and INTERFACE handled
2174 : : // in gjs_value_to_interface_gi_argument()
2175 : 0 : g_warning("Unhandled type %s for JavaScript to GIArgument conversion",
2176 : : g_type_tag_to_string(type_tag));
2177 : 0 : throw_invalid_argument(context, value, type_info, arg_name, arg_type);
2178 : 0 : return false;
2179 : : }
2180 : :
2181 : 12 : return true;
2182 : : }
2183 : :
2184 : 35 : bool gjs_value_to_callback_out_arg(JSContext* context, JS::HandleValue value,
2185 : : GIArgInfo* arg_info, GIArgument* arg) {
2186 : 35 : GIDirection direction [[maybe_unused]] = g_arg_info_get_direction(arg_info);
2187 : 35 : g_assert(
2188 : : (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) &&
2189 : : "gjs_value_to_callback_out_arg does not handle in arguments.");
2190 : :
2191 : 35 : GjsArgumentFlags flags = GjsArgumentFlags::NONE;
2192 : : GITypeInfo type_info;
2193 : :
2194 : 35 : g_arg_info_load_type(arg_info, &type_info);
2195 : :
2196 : : // If the argument is optional and we're passed nullptr,
2197 : : // ignore the GJS value.
2198 [ - + - - : 35 : if (g_arg_info_is_optional(arg_info) && !arg)
- + ]
2199 : 0 : return true;
2200 : :
2201 : : // Otherwise, throw an error to prevent a segfault.
2202 [ - + ]: 35 : if (!arg) {
2203 : 0 : gjs_throw(context,
2204 : : "Return value %s is not optional but was passed NULL",
2205 : : g_base_info_get_name(arg_info));
2206 : 0 : return false;
2207 : : }
2208 : :
2209 [ - + ]: 35 : if (g_arg_info_may_be_null(arg_info))
2210 : 0 : flags |= GjsArgumentFlags::MAY_BE_NULL;
2211 [ + + ]: 35 : if (g_arg_info_is_caller_allocates(arg_info))
2212 : 3 : flags |= GjsArgumentFlags::CALLER_ALLOCATES;
2213 : :
2214 [ - + ]: 70 : return gjs_value_to_gi_argument(
2215 : : context, value, &type_info, g_base_info_get_name(arg_info),
2216 : 35 : (g_arg_info_is_return_value(arg_info) ? GJS_ARGUMENT_RETURN_VALUE
2217 : : : GJS_ARGUMENT_ARGUMENT),
2218 : 35 : g_arg_info_get_ownership_transfer(arg_info), flags, arg);
2219 : : }
2220 : :
2221 : : ///// "FROM" MARSHALLERS ///////////////////////////////////////////////////////
2222 : : // These marshaller functions are responsible for converting C values returned
2223 : : // from a C function call, usually stored in a GIArgument, back to JS values.
2224 : :
2225 : 8890 : bool gjs_value_from_basic_gi_argument(JSContext* cx,
2226 : : JS::MutableHandleValue value_out,
2227 : : GITypeTag type_tag, GIArgument* arg) {
2228 : 8890 : g_assert(GI_TYPE_TAG_IS_BASIC(type_tag) &&
2229 : : "use gjs_value_from_gi_argument() for non-basic types");
2230 : :
2231 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
2232 : : "Converting GIArgument %s to JS::Value",
2233 : : g_type_tag_to_string(type_tag));
2234 : :
2235 [ - + + + : 8890 : switch (type_tag) {
+ + - + -
+ + + + +
+ - ]
2236 : 0 : case GI_TYPE_TAG_VOID:
2237 : : // Pointers are handled in gjs_value_from_gi_argument(), and would
2238 : : // set null instead
2239 : 0 : value_out.setUndefined();
2240 : 0 : return true;
2241 : :
2242 : 68 : case GI_TYPE_TAG_BOOLEAN:
2243 : 68 : value_out.setBoolean(gjs_arg_get<bool>(arg));
2244 : 68 : return true;
2245 : :
2246 : 4845 : case GI_TYPE_TAG_INT32:
2247 : 4845 : value_out.setInt32(gjs_arg_get<int32_t>(arg));
2248 : 4845 : return true;
2249 : :
2250 : 11 : case GI_TYPE_TAG_UINT32:
2251 : 11 : value_out.setNumber(gjs_arg_get<uint32_t>(arg));
2252 : 11 : return true;
2253 : :
2254 : 31 : case GI_TYPE_TAG_INT64:
2255 : 31 : value_out.setNumber(gjs_arg_get_maybe_rounded<int64_t>(arg));
2256 : 31 : return true;
2257 : :
2258 : 4 : case GI_TYPE_TAG_UINT64:
2259 : 4 : value_out.setNumber(gjs_arg_get_maybe_rounded<uint64_t>(arg));
2260 : 4 : return true;
2261 : :
2262 : 0 : case GI_TYPE_TAG_UINT16:
2263 : 0 : value_out.setInt32(gjs_arg_get<uint16_t>(arg));
2264 : 0 : return true;
2265 : :
2266 : 4 : case GI_TYPE_TAG_INT16:
2267 : 4 : value_out.setInt32(gjs_arg_get<int16_t>(arg));
2268 : 4 : return true;
2269 : :
2270 : 0 : case GI_TYPE_TAG_UINT8:
2271 : 0 : value_out.setInt32(gjs_arg_get<uint8_t>(arg));
2272 : 0 : return true;
2273 : :
2274 : 108 : case GI_TYPE_TAG_INT8:
2275 : 108 : value_out.setInt32(gjs_arg_get<int8_t>(arg));
2276 : 108 : return true;
2277 : :
2278 : 7 : case GI_TYPE_TAG_FLOAT:
2279 : 7 : value_out.setNumber(JS::CanonicalizeNaN(gjs_arg_get<float>(arg)));
2280 : 7 : return true;
2281 : :
2282 : 15 : case GI_TYPE_TAG_DOUBLE:
2283 : 15 : value_out.setNumber(JS::CanonicalizeNaN(gjs_arg_get<double>(arg)));
2284 : 15 : return true;
2285 : :
2286 : 1562 : case GI_TYPE_TAG_GTYPE: {
2287 : 1562 : GType gtype = gjs_arg_get<Gjs::Tag::GType>(arg);
2288 [ + + ]: 1562 : if (gtype == 0) {
2289 : 3 : value_out.setNull();
2290 : 3 : return true;
2291 : : }
2292 : :
2293 : 1559 : JSObject* obj = gjs_gtype_create_gtype_wrapper(cx, gtype);
2294 [ - + ]: 1559 : if (!obj)
2295 : 0 : return false;
2296 : :
2297 : 1559 : value_out.setObject(*obj);
2298 : 1559 : return true;
2299 : : }
2300 : :
2301 : 3 : case GI_TYPE_TAG_UNICHAR: {
2302 : 3 : char32_t value = gjs_arg_get<char32_t>(arg);
2303 : :
2304 : : // Preserve the bidirectional mapping between 0 and ""
2305 [ + + ]: 3 : if (value == 0) {
2306 : 1 : value_out.set(JS_GetEmptyStringValue(cx));
2307 : 1 : return true;
2308 [ - + ]: 2 : } else if (!g_unichar_validate(value)) {
2309 : 0 : gjs_throw(cx, "Invalid unicode codepoint %" G_GUINT32_FORMAT,
2310 : : value);
2311 : 0 : return false;
2312 : : }
2313 : :
2314 : : char utf8[7];
2315 : 2 : int bytes = g_unichar_to_utf8(value, utf8);
2316 : 2 : return gjs_string_from_utf8_n(cx, utf8, bytes, value_out);
2317 : : }
2318 : :
2319 : 2232 : case GI_TYPE_TAG_FILENAME:
2320 : : case GI_TYPE_TAG_UTF8: {
2321 : 2232 : const char* str = gjs_arg_get<const char*>(arg);
2322 [ + + ]: 2232 : if (!str) {
2323 : 99 : value_out.setNull();
2324 : 99 : return true;
2325 : : }
2326 : :
2327 [ + + ]: 2133 : if (type_tag == GI_TYPE_TAG_FILENAME)
2328 : 12 : return gjs_string_from_filename(cx, str, -1, value_out);
2329 : :
2330 : 2121 : return gjs_string_from_utf8(cx, str, value_out);
2331 : : }
2332 : :
2333 : 0 : default:
2334 : : // this function handles only basic types
2335 : : g_return_val_if_reached(false);
2336 : : }
2337 : : }
2338 : :
2339 : 13 : bool gjs_array_from_strv(JSContext* cx, JS::MutableHandleValue value_out,
2340 : : const char** strv) {
2341 : : // We treat a NULL strv as an empty array, since this function should always
2342 : : // set an array value when returning true. Another alternative would be
2343 : : // value_out.setNull(), but clients would need to always check for both an
2344 : : // empty array and null if that was the case.
2345 : 13 : JS::RootedValueVector elems{cx};
2346 [ + + + + ]: 38 : for (size_t i = 0; strv && strv[i]; i++) {
2347 [ - + ]: 25 : if (!elems.growBy(1)) {
2348 : 0 : JS_ReportOutOfMemory(cx);
2349 : 0 : return false;
2350 : : }
2351 : :
2352 [ - + ]: 25 : if (!gjs_string_from_utf8(cx, strv[i], elems[i]))
2353 : 0 : return false;
2354 : : }
2355 : :
2356 : 13 : JSObject* obj = JS::NewArrayObject(cx, elems);
2357 [ - + ]: 13 : if (!obj)
2358 : 0 : return false;
2359 : :
2360 : 13 : value_out.setObject(*obj);
2361 : 13 : return true;
2362 : 13 : }
2363 : :
2364 : : template <typename T>
2365 : 76 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_from_g_list(
2366 : : JSContext* cx, JS::MutableHandleValue value_p, GITypeInfo* type_info,
2367 : : GITransfer transfer, T* list) {
2368 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
2369 : 76 : JS::RootedValueVector elems(cx);
2370 : 76 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
2371 : :
2372 : 76 : g_assert(param_info);
2373 : :
2374 : : GIArgument arg;
2375 [ + + ]: 261 : for (size_t i = 0; list; list = list->next, ++i) {
2376 : 185 : g_type_info_argument_from_hash_pointer(param_info, list->data, &arg);
2377 : :
2378 [ - + ]: 185 : if (!elems.growBy(1)) {
2379 : 0 : JS_ReportOutOfMemory(cx);
2380 : 0 : return false;
2381 : : }
2382 : :
2383 [ - + ]: 185 : if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
2384 : : GJS_ARGUMENT_LIST_ELEMENT, transfer,
2385 : : &arg))
2386 : 0 : return false;
2387 : : }
2388 : :
2389 : 76 : JSObject* obj = JS::NewArrayObject(cx, elems);
2390 [ - + ]: 76 : if (!obj)
2391 : 0 : return false;
2392 : :
2393 : 76 : value_p.setObject(*obj);
2394 : :
2395 : 76 : return true;
2396 : 76 : }
2397 : :
2398 : : template <typename TAG>
2399 : 75 : GJS_JSAPI_RETURN_CONVENTION static bool fill_vector_from_basic_c_array(
2400 : : JSContext* cx, JS::MutableHandleValueVector elems, GITypeTag element_tag,
2401 : : GIArgument* arg, void* array, size_t length) {
2402 : : using T = Gjs::Tag::RealT<TAG>;
2403 [ + + ]: 406 : for (size_t i = 0; i < length; i++) {
2404 : 331 : gjs_arg_set<TAG>(arg, *(static_cast<T*>(array) + i));
2405 : :
2406 [ - + ]: 331 : if (!gjs_value_from_basic_gi_argument(cx, elems[i], element_tag, arg))
2407 : 0 : return false;
2408 : : }
2409 : :
2410 : 75 : return true;
2411 : : }
2412 : :
2413 : : template <typename T>
2414 : 25 : GJS_JSAPI_RETURN_CONVENTION static bool fill_vector_from_carray(
2415 : : JSContext* cx, JS::RootedValueVector& elems, // NOLINT(runtime/references)
2416 : : GITypeInfo* param_info, GIArgument* arg, void* array, size_t length,
2417 : : GITransfer transfer = GI_TRANSFER_EVERYTHING) {
2418 [ + + ]: 101 : for (size_t i = 0; i < length; i++) {
2419 : 76 : gjs_arg_set<T>(arg, *(static_cast<Gjs::Tag::RealT<T>*>(array) + i));
2420 : :
2421 [ - + ]: 76 : if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
2422 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
2423 : : arg))
2424 : 0 : return false;
2425 : : }
2426 : :
2427 : 25 : return true;
2428 : : }
2429 : :
2430 : : GJS_JSAPI_RETURN_CONVENTION
2431 : 97 : static bool gjs_array_from_basic_c_array_internal(
2432 : : JSContext* cx, JS::MutableHandleValue value_out, GITypeTag element_tag,
2433 : : size_t length, void* contents) {
2434 : 97 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag));
2435 : :
2436 : : // Special case array(uint8)
2437 [ + + ]: 97 : if (element_tag == GI_TYPE_TAG_UINT8) {
2438 : 9 : JSObject* u8array = gjs_byte_array_from_data_copy(cx, length, contents);
2439 [ - + ]: 9 : if (!u8array)
2440 : 0 : return false;
2441 : 9 : value_out.setObject(*u8array);
2442 : 9 : return true;
2443 : : }
2444 : :
2445 : : // Special case array(unichar) to be a string in JS
2446 [ + + ]: 88 : if (element_tag == GI_TYPE_TAG_UNICHAR) {
2447 : 1 : return gjs_string_from_ucs4(cx, static_cast<gunichar*>(contents),
2448 : 1 : length, value_out);
2449 : : }
2450 : :
2451 : : // a null array pointer takes precedence over whatever `length` says
2452 [ + + ]: 87 : if (!contents) {
2453 : 12 : JSObject* array = JS::NewArrayObject(cx, 0);
2454 [ - + ]: 12 : if (!array)
2455 : 0 : return false;
2456 : 12 : value_out.setObject(*array);
2457 : 12 : return true;
2458 : : }
2459 : :
2460 : 75 : JS::RootedValueVector elems{cx};
2461 [ - + ]: 75 : if (!elems.resize(length)) {
2462 : 0 : JS_ReportOutOfMemory(cx);
2463 : 0 : return false;
2464 : : }
2465 : :
2466 : : GIArgument arg;
2467 [ - + + - : 75 : switch (element_tag) {
+ + + + -
- - + -
- ]
2468 : : // Special cases handled above
2469 : 0 : case GI_TYPE_TAG_UINT8:
2470 : : case GI_TYPE_TAG_UNICHAR:
2471 : : g_assert_not_reached();
2472 : :
2473 : 1 : case GI_TYPE_TAG_BOOLEAN:
2474 [ - + ]: 1 : if (!fill_vector_from_basic_c_array<Gjs::Tag::GBoolean>(
2475 : : cx, &elems, element_tag, &arg, contents, length))
2476 : 0 : return false;
2477 : 1 : break;
2478 : 2 : case GI_TYPE_TAG_INT8:
2479 [ - + ]: 2 : if (!fill_vector_from_basic_c_array<int8_t>(cx, &elems, element_tag,
2480 : : &arg, contents, length))
2481 : 0 : return false;
2482 : 2 : break;
2483 : 0 : case GI_TYPE_TAG_UINT16:
2484 [ # # ]: 0 : if (!fill_vector_from_basic_c_array<uint16_t>(
2485 : : cx, &elems, element_tag, &arg, contents, length))
2486 : 0 : return false;
2487 : 0 : break;
2488 : 1 : case GI_TYPE_TAG_INT16:
2489 [ - + ]: 1 : if (!fill_vector_from_basic_c_array<int16_t>(
2490 : : cx, &elems, element_tag, &arg, contents, length))
2491 : 0 : return false;
2492 : 1 : break;
2493 : 1 : case GI_TYPE_TAG_UINT32:
2494 [ - + ]: 1 : if (!fill_vector_from_basic_c_array<uint32_t>(
2495 : : cx, &elems, element_tag, &arg, contents, length))
2496 : 0 : return false;
2497 : 1 : break;
2498 : 23 : case GI_TYPE_TAG_INT32:
2499 [ - + ]: 23 : if (!fill_vector_from_basic_c_array<int32_t>(
2500 : : cx, &elems, element_tag, &arg, contents, length))
2501 : 0 : return false;
2502 : 23 : break;
2503 : 1 : case GI_TYPE_TAG_UINT64:
2504 [ - + ]: 1 : if (!fill_vector_from_basic_c_array<uint64_t>(
2505 : : cx, &elems, element_tag, &arg, contents, length))
2506 : 0 : return false;
2507 : 1 : break;
2508 : 0 : case GI_TYPE_TAG_INT64:
2509 [ # # ]: 0 : if (!fill_vector_from_basic_c_array<int64_t>(
2510 : : cx, &elems, element_tag, &arg, contents, length))
2511 : 0 : return false;
2512 : 0 : break;
2513 : 0 : case GI_TYPE_TAG_FLOAT:
2514 [ # # ]: 0 : if (!fill_vector_from_basic_c_array<float>(cx, &elems, element_tag,
2515 : : &arg, contents, length))
2516 : 0 : return false;
2517 : 0 : break;
2518 : 0 : case GI_TYPE_TAG_DOUBLE:
2519 [ # # ]: 0 : if (!fill_vector_from_basic_c_array<double>(cx, &elems, element_tag,
2520 : : &arg, contents, length))
2521 : 0 : return false;
2522 : 0 : break;
2523 : :
2524 : 46 : case GI_TYPE_TAG_GTYPE:
2525 : : case GI_TYPE_TAG_UTF8:
2526 : : case GI_TYPE_TAG_FILENAME:
2527 [ - + ]: 46 : if (!fill_vector_from_basic_c_array<void*>(cx, &elems, element_tag,
2528 : : &arg, contents, length))
2529 : 0 : return false;
2530 : 46 : break;
2531 : 0 : case GI_TYPE_TAG_VOID:
2532 : 0 : gjs_throw(cx, "Unknown Array element-type %d", element_tag);
2533 : 0 : return false;
2534 : 0 : default:
2535 : : g_assert_not_reached();
2536 : : }
2537 : :
2538 : 75 : JSObject* array = JS::NewArrayObject(cx, elems);
2539 [ - + ]: 75 : if (!array)
2540 : 0 : return false;
2541 : :
2542 : 75 : value_out.setObject(*array);
2543 : 75 : return true;
2544 : 75 : }
2545 : :
2546 : : GJS_JSAPI_RETURN_CONVENTION
2547 : 43 : static bool gjs_array_from_carray_internal(
2548 : : JSContext* context, JS::MutableHandleValue value_p, GIArrayType array_type,
2549 : : GITypeInfo* param_info, GITransfer transfer, guint length, void* array) {
2550 : : GITypeTag element_type;
2551 : : guint i;
2552 : :
2553 : 43 : element_type = g_type_info_get_tag(param_info);
2554 : :
2555 [ + + - + ]: 43 : if (GI_TYPE_TAG_IS_BASIC(element_type)) {
2556 : 10 : return gjs_array_from_basic_c_array_internal(
2557 : 10 : context, value_p, element_type, length, array);
2558 : : }
2559 : :
2560 : : // a null array pointer takes precedence over whatever `length` says
2561 [ - + ]: 33 : if (!array) {
2562 : 0 : JSObject* jsarray = JS::NewArrayObject(context, 0);
2563 [ # # ]: 0 : if (!jsarray)
2564 : 0 : return false;
2565 : 0 : value_p.setObject(*jsarray);
2566 : 0 : return true;
2567 : : }
2568 : :
2569 : 33 : JS::RootedValueVector elems(context);
2570 [ - + ]: 33 : if (!elems.resize(length)) {
2571 : 0 : JS_ReportOutOfMemory(context);
2572 : 0 : return false;
2573 : : }
2574 : :
2575 : : GIArgument arg;
2576 [ + + - ]: 33 : switch (element_type) {
2577 : 17 : case GI_TYPE_TAG_INTERFACE: {
2578 : : GI::AutoBaseInfo interface_info{
2579 : 17 : g_type_info_get_interface(param_info)};
2580 : :
2581 [ + + ]: 13 : if (array_type != GI_ARRAY_TYPE_PTR_ARRAY &&
2582 [ - + ]: 15 : (GI_IS_STRUCT_INFO(interface_info) ||
2583 [ + + + + : 32 : GI_IS_UNION_INFO(interface_info)) &&
+ + ]
2584 : 11 : !g_type_info_is_pointer(param_info)) {
2585 : : size_t struct_size;
2586 : :
2587 [ - + ]: 8 : if (GI_IS_UNION_INFO(interface_info))
2588 : 0 : struct_size = g_union_info_get_size(interface_info);
2589 : : else
2590 : 8 : struct_size = g_struct_info_get_size(interface_info);
2591 : :
2592 [ + + ]: 35 : for (i = 0; i < length; i++) {
2593 : 27 : gjs_arg_set(&arg,
2594 : 27 : static_cast<char*>(array) + (struct_size * i));
2595 : :
2596 [ - + ]: 27 : if (!gjs_value_from_gi_argument(
2597 : : context, elems[i], param_info,
2598 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer, &arg))
2599 : 0 : return false;
2600 : : }
2601 : :
2602 : 8 : break;
2603 : : }
2604 [ + - + ]: 17 : }
2605 : : /* fallthrough */
2606 : : case GI_TYPE_TAG_ARRAY:
2607 : : case GI_TYPE_TAG_GLIST:
2608 : : case GI_TYPE_TAG_GSLIST:
2609 : : case GI_TYPE_TAG_GHASH:
2610 : : case GI_TYPE_TAG_ERROR:
2611 [ - + ]: 25 : if (!fill_vector_from_carray<void*>(context, elems, param_info,
2612 : : &arg, array, length, transfer))
2613 : 0 : return false;
2614 : 25 : break;
2615 : 0 : default:
2616 : : // Basic types handled above
2617 : 0 : gjs_throw(context, "Unknown Array element-type %d", element_type);
2618 : 0 : return false;
2619 : : }
2620 : :
2621 : 33 : JSObject* obj = JS::NewArrayObject(context, elems);
2622 [ - + ]: 33 : if (!obj)
2623 : 0 : return false;
2624 : :
2625 : 33 : value_p.setObject(*obj);
2626 : :
2627 : 33 : return true;
2628 : 33 : }
2629 : :
2630 : : GJS_JSAPI_RETURN_CONVENTION
2631 : 14 : static bool gjs_array_from_fixed_size_array(JSContext* context,
2632 : : JS::MutableHandleValue value_p,
2633 : : GITypeInfo* type_info,
2634 : : GITransfer transfer, void* array) {
2635 : : gint length;
2636 : :
2637 : 14 : length = g_type_info_get_array_fixed_size(type_info);
2638 : :
2639 : 14 : g_assert (length != -1);
2640 : :
2641 : 14 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
2642 : :
2643 : 14 : return gjs_array_from_carray_internal(context, value_p,
2644 : : g_type_info_get_array_type(type_info),
2645 : 14 : param_info, transfer, length, array);
2646 : 14 : }
2647 : :
2648 : 21 : bool gjs_value_from_explicit_array(JSContext* context,
2649 : : JS::MutableHandleValue value_p,
2650 : : GITypeInfo* type_info, GITransfer transfer,
2651 : : GIArgument* arg, int length) {
2652 : 21 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
2653 : :
2654 : 21 : return gjs_array_from_carray_internal(
2655 : : context, value_p, g_type_info_get_array_type(type_info), param_info,
2656 : 21 : transfer, length, gjs_arg_get<void*>(arg));
2657 : 21 : }
2658 : :
2659 : 49 : bool gjs_value_from_basic_explicit_array(JSContext* cx,
2660 : : JS::MutableHandleValue value_out,
2661 : : GITypeTag element_tag, GIArgument* arg,
2662 : : size_t length) {
2663 : 49 : return gjs_array_from_basic_c_array_internal(
2664 : 49 : cx, value_out, element_tag, length, gjs_arg_get<void*>(arg));
2665 : : }
2666 : :
2667 : : GJS_JSAPI_RETURN_CONVENTION
2668 : 2 : static bool gjs_array_from_boxed_array(JSContext* context,
2669 : : JS::MutableHandleValue value_p,
2670 : : GIArrayType array_type,
2671 : : GITypeInfo* param_info,
2672 : : GITransfer transfer, GIArgument* arg) {
2673 : : GArray *array;
2674 : : GPtrArray *ptr_array;
2675 : 2 : gpointer data = NULL;
2676 : 2 : gsize length = 0;
2677 : :
2678 [ - + ]: 2 : if (!gjs_arg_get<void*>(arg)) {
2679 : 0 : value_p.setNull();
2680 : 0 : return true;
2681 : : }
2682 : :
2683 [ + + - ]: 2 : switch(array_type) {
2684 : 1 : case GI_ARRAY_TYPE_BYTE_ARRAY:
2685 : : /* GByteArray is just a typedef for GArray internally */
2686 : : case GI_ARRAY_TYPE_ARRAY:
2687 : 1 : array = gjs_arg_get<GArray*>(arg);
2688 : 1 : data = array->data;
2689 : 1 : length = array->len;
2690 : 1 : break;
2691 : 1 : case GI_ARRAY_TYPE_PTR_ARRAY:
2692 : 1 : ptr_array = gjs_arg_get<GPtrArray*>(arg);
2693 : 1 : data = ptr_array->pdata;
2694 : 1 : length = ptr_array->len;
2695 : 1 : break;
2696 : 0 : case GI_ARRAY_TYPE_C: // already checked in gjs_value_from_gi_argument()
2697 : : default:
2698 : : g_assert_not_reached();
2699 : : }
2700 : :
2701 : 2 : return gjs_array_from_carray_internal(context, value_p, array_type,
2702 : 2 : param_info, transfer, length, data);
2703 : : }
2704 : :
2705 : : GJS_JSAPI_RETURN_CONVENTION
2706 : 6 : bool gjs_array_from_g_value_array(JSContext* cx, JS::MutableHandleValue value_p,
2707 : : GITypeInfo* param_info, GITransfer transfer,
2708 : : const GValue* gvalue) {
2709 : 6 : void* data = nullptr;
2710 : 6 : size_t length = 0;
2711 : : GIArrayType array_type;
2712 : 6 : GType value_gtype = G_VALUE_TYPE(gvalue);
2713 : :
2714 : : // GByteArray is just a typedef for GArray internally
2715 [ + - + - : 12 : if (g_type_is_a(value_gtype, G_TYPE_BYTE_ARRAY) ||
- + ]
2716 [ + - - + ]: 6 : g_type_is_a(value_gtype, G_TYPE_ARRAY)) {
2717 [ # # ]: 0 : array_type = g_type_is_a(value_gtype, G_TYPE_BYTE_ARRAY)
2718 [ # # ]: 0 : ? GI_ARRAY_TYPE_BYTE_ARRAY
2719 : : : GI_ARRAY_TYPE_ARRAY;
2720 : 0 : auto* array = Gjs::gvalue_get<GArray*>(gvalue);
2721 : 0 : data = array->data;
2722 : 0 : length = array->len;
2723 [ - + - - : 6 : } else if (g_type_is_a(value_gtype, G_TYPE_PTR_ARRAY)) {
+ - ]
2724 : 6 : array_type = GI_ARRAY_TYPE_PTR_ARRAY;
2725 : 6 : auto* ptr_array = Gjs::gvalue_get<GPtrArray*>(gvalue);
2726 : 6 : data = ptr_array->pdata;
2727 : 6 : length = ptr_array->len;
2728 : : } else {
2729 : : g_assert_not_reached();
2730 : : gjs_throw(cx, "%s is not an array type", g_type_name(value_gtype));
2731 : : return false;
2732 : : }
2733 : :
2734 : 6 : return gjs_array_from_carray_internal(cx, value_p, array_type, param_info,
2735 : 6 : transfer, length, data);
2736 : : }
2737 : :
2738 : : template <typename TAG>
2739 : : GJS_JSAPI_RETURN_CONVENTION static bool
2740 : 121 : fill_vector_from_basic_zero_terminated_c_array(
2741 : : JSContext* cx, JS::MutableHandleValueVector elems, GITypeTag element_tag,
2742 : : GIArgument* arg, void* c_array) {
2743 : : using T = Gjs::Tag::RealT<TAG>;
2744 : 121 : T* array = static_cast<T*>(c_array);
2745 : :
2746 [ + + ]: 499 : for (size_t ix = 0; array[ix]; ix++) {
2747 : 378 : gjs_arg_set<TAG>(arg, array[ix]);
2748 : :
2749 [ - + ]: 378 : if (!elems.growBy(1)) {
2750 : 0 : JS_ReportOutOfMemory(cx);
2751 : 0 : return false;
2752 : : }
2753 : :
2754 [ - + ]: 378 : if (!gjs_value_from_basic_gi_argument(cx, elems[ix], element_tag, arg))
2755 : 0 : return false;
2756 : : }
2757 : :
2758 : 121 : return true;
2759 : : }
2760 : :
2761 : : template <typename T>
2762 : 426 : GJS_JSAPI_RETURN_CONVENTION static bool fill_vector_from_zero_terminated_carray(
2763 : : JSContext* cx, JS::RootedValueVector& elems, // NOLINT(runtime/references)
2764 : : GITypeInfo* param_info, GIArgument* arg, void* c_array,
2765 : : GITransfer transfer = GI_TRANSFER_EVERYTHING) {
2766 : 426 : T* array = static_cast<T*>(c_array);
2767 : :
2768 : 995 : for (size_t i = 0;; i++) {
2769 : : if constexpr (std::is_scalar_v<T>) {
2770 [ + + ]: 991 : if (!array[i])
2771 : 425 : break;
2772 : :
2773 : 566 : gjs_arg_set(arg, array[i]);
2774 : : } else {
2775 : 4 : uint8_t* element_start = reinterpret_cast<uint8_t*>(&array[i]);
2776 [ + + ]: 4 : if (*element_start == 0 &&
2777 : : // cppcheck-suppress pointerSize
2778 [ + - ]: 1 : memcmp(element_start, element_start + 1, sizeof(T) - 1) == 0)
2779 : 1 : break;
2780 : :
2781 : 3 : gjs_arg_set(arg, element_start);
2782 : : }
2783 : :
2784 [ - + ]: 569 : if (!elems.growBy(1)) {
2785 : 0 : JS_ReportOutOfMemory(cx);
2786 : 0 : return false;
2787 : : }
2788 : :
2789 [ - + ]: 569 : if (!gjs_value_from_gi_argument(cx, elems[i], param_info,
2790 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
2791 : : arg))
2792 : 0 : return false;
2793 : : }
2794 : :
2795 : 426 : return true;
2796 : : }
2797 : :
2798 : 129 : bool gjs_array_from_basic_zero_terminated_array(
2799 : : JSContext* cx, JS::MutableHandleValue value_out, GITypeTag element_tag,
2800 : : void* c_array) {
2801 : 129 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag) &&
2802 : : "Use gjs_array_from_zero_terminated_c_array for non-basic types");
2803 : :
2804 [ + + ]: 129 : if (!c_array) {
2805 : : // OK, but no conversion to do
2806 : 5 : value_out.setNull();
2807 : 5 : return true;
2808 : : }
2809 : :
2810 : : // Special case array(uint8_t)
2811 [ + + ]: 124 : if (element_tag == GI_TYPE_TAG_UINT8) {
2812 : 2 : size_t length = strlen(static_cast<char*>(c_array));
2813 : : JSObject* byte_array =
2814 : 2 : gjs_byte_array_from_data_copy(cx, length, c_array);
2815 [ - + ]: 2 : if (!byte_array)
2816 : 0 : return false;
2817 : :
2818 : 2 : value_out.setObject(*byte_array);
2819 : 2 : return true;
2820 : : }
2821 : :
2822 : : // Special case array(gunichar) to JS string
2823 [ + + ]: 122 : if (element_tag == GI_TYPE_TAG_UNICHAR) {
2824 : 1 : return gjs_string_from_ucs4(cx, static_cast<gunichar*>(c_array), -1,
2825 : 1 : value_out);
2826 : : }
2827 : :
2828 : 121 : JS::RootedValueVector elems{cx};
2829 : :
2830 : : GIArgument arg;
2831 [ - - - - : 121 : switch (element_tag) {
- - - + -
+ - - - ]
2832 : 0 : case GI_TYPE_TAG_INT8:
2833 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<int8_t>(
2834 : : cx, &elems, element_tag, &arg, c_array))
2835 : 0 : return false;
2836 : 0 : break;
2837 : 0 : case GI_TYPE_TAG_UINT16:
2838 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<uint16_t>(
2839 : : cx, &elems, element_tag, &arg, c_array))
2840 : 0 : return false;
2841 : 0 : break;
2842 : 0 : case GI_TYPE_TAG_INT16:
2843 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<int16_t>(
2844 : : cx, &elems, element_tag, &arg, c_array))
2845 : 0 : return false;
2846 : 0 : break;
2847 : 0 : case GI_TYPE_TAG_UINT32:
2848 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<uint32_t>(
2849 : : cx, &elems, element_tag, &arg, c_array))
2850 : 0 : return false;
2851 : 0 : break;
2852 : 0 : case GI_TYPE_TAG_INT32:
2853 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<int32_t>(
2854 : : cx, &elems, element_tag, &arg, c_array))
2855 : 0 : return false;
2856 : 0 : break;
2857 : 0 : case GI_TYPE_TAG_UINT64:
2858 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<uint64_t>(
2859 : : cx, &elems, element_tag, &arg, c_array))
2860 : 0 : return false;
2861 : 0 : break;
2862 : 0 : case GI_TYPE_TAG_INT64:
2863 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<int64_t>(
2864 : : cx, &elems, element_tag, &arg, c_array))
2865 : 0 : return false;
2866 : 0 : break;
2867 : 1 : case GI_TYPE_TAG_FLOAT:
2868 [ - + ]: 1 : if (!fill_vector_from_basic_zero_terminated_c_array<float>(
2869 : : cx, &elems, element_tag, &arg, c_array))
2870 : 0 : return false;
2871 : 1 : break;
2872 : 0 : case GI_TYPE_TAG_DOUBLE:
2873 [ # # ]: 0 : if (!fill_vector_from_basic_zero_terminated_c_array<double>(
2874 : : cx, &elems, element_tag, &arg, c_array))
2875 : 0 : return false;
2876 : 0 : break;
2877 : 120 : case GI_TYPE_TAG_GTYPE:
2878 : : case GI_TYPE_TAG_UTF8:
2879 : : case GI_TYPE_TAG_FILENAME:
2880 [ - + ]: 120 : if (!fill_vector_from_basic_zero_terminated_c_array<void*>(
2881 : : cx, &elems, element_tag, &arg, c_array))
2882 : 0 : return false;
2883 : 120 : break;
2884 : : // Boolean zero-terminated array makes no sense, because false is also
2885 : : // zero
2886 : 0 : case GI_TYPE_TAG_BOOLEAN:
2887 : 0 : gjs_throw(cx, "Boolean zero-terminated array not supported");
2888 : 0 : return false;
2889 : 0 : case GI_TYPE_TAG_VOID:
2890 : 0 : gjs_throw(cx, "Unknown element-type 'void'");
2891 : 0 : return false;
2892 : 0 : default:
2893 : : // UINT8 and UNICHAR are special cases handled above
2894 : : g_assert_not_reached();
2895 : : }
2896 : :
2897 : 121 : JSObject* array = JS::NewArrayObject(cx, elems);
2898 [ - + ]: 121 : if (!array)
2899 : 0 : return false;
2900 : :
2901 : 121 : value_out.setObject(*array);
2902 : 121 : return true;
2903 : 121 : }
2904 : :
2905 : : GJS_JSAPI_RETURN_CONVENTION
2906 : 426 : static bool gjs_array_from_zero_terminated_c_array(
2907 : : JSContext* context, JS::MutableHandleValue value_p, GITypeInfo* param_info,
2908 : : GITransfer transfer, void* c_array) {
2909 : : GITypeTag element_type;
2910 : :
2911 : 426 : element_type = g_type_info_get_tag(param_info);
2912 : 426 : bool element_is_pointer = g_type_info_is_pointer(param_info);
2913 : :
2914 [ - + ]: 426 : if (Gjs::is_basic_type(element_type, element_is_pointer)) {
2915 : 0 : return gjs_array_from_basic_zero_terminated_array(
2916 : 0 : context, value_p, element_type, c_array);
2917 : : }
2918 : :
2919 : 426 : JS::RootedValueVector elems(context);
2920 : :
2921 : : GIArgument arg;
2922 [ + + - ]: 426 : switch (element_type) {
2923 : 418 : case GI_TYPE_TAG_INTERFACE: {
2924 : : GI::AutoBaseInfo interface_info{
2925 : 418 : g_type_info_get_interface(param_info)};
2926 : :
2927 [ + + + - : 418 : if (!element_is_pointer && is_gvalue(interface_info)) {
+ + ]
2928 [ - + ]: 1 : if (!fill_vector_from_zero_terminated_carray<GValue>(
2929 : : context, elems, param_info, &arg, c_array))
2930 : 0 : return false;
2931 : 1 : break;
2932 : : }
2933 : :
2934 [ - + ]: 417 : if (!element_is_pointer) {
2935 : 0 : gjs_throw(context,
2936 : : "Flat C array of %s.%s not supported (see "
2937 : : "https://gitlab.gnome.org/GNOME/gjs/-/issues/603)",
2938 : : interface_info.ns(), interface_info.name());
2939 : 0 : return false;
2940 : : }
2941 : :
2942 : : [[fallthrough]];
2943 [ + - + ]: 418 : }
2944 : : case GI_TYPE_TAG_ARRAY:
2945 : : case GI_TYPE_TAG_GLIST:
2946 : : case GI_TYPE_TAG_GSLIST:
2947 : : case GI_TYPE_TAG_GHASH:
2948 : : case GI_TYPE_TAG_ERROR:
2949 [ - + ]: 425 : if (!fill_vector_from_zero_terminated_carray<void*>(
2950 : : context, elems, param_info, &arg, c_array, transfer))
2951 : 0 : return false;
2952 : 425 : break;
2953 : 0 : default:
2954 : : // Handled in gjs_array_from_basic_zero_terminated_c_array()
2955 : 0 : gjs_throw(context, "Unknown element-type %d", element_type);
2956 : 0 : return false;
2957 : : }
2958 : :
2959 : 426 : JSObject* obj = JS::NewArrayObject(context, elems);
2960 [ - + ]: 426 : if (!obj)
2961 : 0 : return false;
2962 : :
2963 : 426 : value_p.setObject(*obj);
2964 : :
2965 : 426 : return true;
2966 : 426 : }
2967 : :
2968 : 5 : bool gjs_object_from_g_hash(JSContext* context, JS::MutableHandleValue value_p,
2969 : : GITypeInfo* key_param_info,
2970 : : GITypeInfo* val_param_info, GITransfer transfer,
2971 : : GHashTable* hash) {
2972 : : GHashTableIter iter;
2973 : :
2974 : 5 : g_assert((!GI_TYPE_TAG_IS_BASIC(g_type_info_get_tag(key_param_info)) ||
2975 : : !GI_TYPE_TAG_IS_BASIC(g_type_info_get_tag(val_param_info))) &&
2976 : : "use gjs_value_from_basic_ghash() instead");
2977 : :
2978 : : // a NULL hash table becomes a null JS value
2979 [ - + ]: 5 : if (hash==NULL) {
2980 : 0 : value_p.setNull();
2981 : 0 : return true;
2982 : : }
2983 : :
2984 : 5 : JS::RootedObject obj(context, JS_NewPlainObject(context));
2985 [ - + ]: 5 : if (!obj)
2986 : 0 : return false;
2987 : :
2988 : 5 : value_p.setObject(*obj);
2989 : :
2990 : 5 : JS::RootedValue keyjs(context), valjs(context);
2991 : 5 : JS::RootedString keystr(context);
2992 : :
2993 : 5 : g_hash_table_iter_init(&iter, hash);
2994 : : void* key_pointer;
2995 : : void* val_pointer;
2996 : : GIArgument keyarg, valarg;
2997 [ + + ]: 18 : while (g_hash_table_iter_next(&iter, &key_pointer, &val_pointer)) {
2998 : 13 : g_type_info_argument_from_hash_pointer(key_param_info, key_pointer,
2999 : : &keyarg);
3000 [ - + ]: 13 : if (!gjs_value_from_gi_argument(context, &keyjs, key_param_info,
3001 : : GJS_ARGUMENT_HASH_ELEMENT, transfer,
3002 : : &keyarg))
3003 : 0 : return false;
3004 : :
3005 : 13 : JS::RootedId key{context};
3006 [ - + ]: 13 : if (!JS_ValueToId(context, keyjs, &key))
3007 : 0 : return false;
3008 : :
3009 : 13 : g_type_info_argument_from_hash_pointer(val_param_info, val_pointer,
3010 : : &valarg);
3011 : 13 : if (!gjs_value_from_gi_argument(context, &valjs, val_param_info,
3012 : : GJS_ARGUMENT_HASH_ELEMENT, transfer,
3013 [ + - ]: 26 : &valarg) ||
3014 [ - + - + ]: 26 : !JS_DefinePropertyById(context, obj, key, valjs, JSPROP_ENUMERATE))
3015 : 0 : return false;
3016 [ + - ]: 13 : }
3017 : :
3018 : 5 : return true;
3019 : 5 : }
3020 : :
3021 : 21 : bool gjs_value_from_basic_fixed_size_array_gi_argument(
3022 : : JSContext* cx, JS::MutableHandleValue value_out, GITypeTag element_tag,
3023 : : size_t fixed_size, GIArgument* arg) {
3024 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3025 : : "Converting GIArgument fixed array of %s to JS::Value",
3026 : : g_type_tag_to_string(element_tag));
3027 : :
3028 : 21 : void* c_array = gjs_arg_get<void*>(arg);
3029 [ + + ]: 21 : if (!c_array) {
3030 : : // OK, but no conversion to do
3031 : 1 : value_out.setNull();
3032 : 1 : return true;
3033 : : }
3034 : :
3035 : 20 : return gjs_array_from_basic_c_array_internal(cx, value_out, element_tag,
3036 : 20 : fixed_size, c_array);
3037 : : }
3038 : :
3039 : 4 : bool gjs_value_from_byte_array_gi_argument(JSContext* cx,
3040 : : JS::MutableHandleValue value_out,
3041 : : GIArgument* arg) {
3042 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3043 : : "Converting GIArgument byte array to JS::Value");
3044 : :
3045 : 4 : auto* byte_array = gjs_arg_get<GByteArray*>(arg);
3046 [ - + ]: 4 : if (!byte_array) {
3047 : 0 : value_out.setNull();
3048 : 0 : return true;
3049 : : }
3050 : :
3051 : 4 : JSObject* u8array = gjs_byte_array_from_byte_array(cx, byte_array);
3052 [ - + ]: 4 : if (!u8array)
3053 : 0 : return false;
3054 : :
3055 : 4 : value_out.setObject(*u8array);
3056 : 4 : return true;
3057 : : }
3058 : :
3059 : 13 : bool gjs_value_from_basic_garray_gi_argument(JSContext* cx,
3060 : : JS::MutableHandleValue value_out,
3061 : : GITypeTag element_tag,
3062 : : GIArgument* arg) {
3063 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3064 : : "Converting GIArgument GArray of %s to JS::Value",
3065 : : g_type_tag_to_string(element_tag));
3066 : :
3067 : 13 : auto* garray = gjs_arg_get<GArray*>(arg);
3068 [ + + ]: 13 : if (!garray) {
3069 : 4 : value_out.setNull();
3070 : 4 : return true;
3071 : : }
3072 : :
3073 : 9 : return gjs_array_from_basic_c_array_internal(cx, value_out, element_tag,
3074 : 9 : garray->len, garray->data);
3075 : : }
3076 : :
3077 : 12 : bool gjs_value_from_basic_gptrarray_gi_argument(
3078 : : JSContext* cx, JS::MutableHandleValue value_out, GITypeTag element_tag,
3079 : : GIArgument* arg) {
3080 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3081 : : "Converting GIArgument GPtrArray of %s to JS::Value",
3082 : : g_type_tag_to_string(element_tag));
3083 : :
3084 : 12 : auto* ptr_array = gjs_arg_get<GPtrArray*>(arg);
3085 [ + + ]: 12 : if (!ptr_array) {
3086 : 3 : value_out.setNull();
3087 : 3 : return true;
3088 : : }
3089 : :
3090 : 9 : return gjs_array_from_basic_c_array_internal(
3091 : 9 : cx, value_out, element_tag, ptr_array->len, ptr_array->pdata);
3092 : : }
3093 : :
3094 : : template <typename T>
3095 : 36 : GJS_JSAPI_RETURN_CONVENTION static bool array_from_basic_linked_list(
3096 : : JSContext* cx, JS::MutableHandleValue value_out, GITypeTag element_tag,
3097 : : T* list) {
3098 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
3099 : 36 : g_assert(
3100 : : GI_TYPE_TAG_IS_BASIC(element_tag) &&
3101 : : "use gjs_array_from_g_list() for lists containing non-basic types");
3102 : :
3103 : : GIArgument arg;
3104 : 36 : JS::RootedValueVector elems{cx};
3105 : :
3106 [ + + ]: 118 : for (size_t i = 0; list; list = list->next, ++i) {
3107 : : // for basic types, type tag == storage type
3108 : 82 : gi_type_tag_argument_from_hash_pointer(element_tag, list->data, &arg);
3109 : :
3110 [ - + ]: 82 : if (!elems.growBy(1)) {
3111 : 0 : JS_ReportOutOfMemory(cx);
3112 : 0 : return false;
3113 : : }
3114 : :
3115 [ - + ]: 82 : if (!gjs_value_from_basic_gi_argument(cx, elems[i], element_tag, &arg))
3116 : 0 : return false;
3117 : : }
3118 : :
3119 : 36 : JS::RootedObject obj{cx, JS::NewArrayObject(cx, elems)};
3120 [ - + ]: 36 : if (!obj)
3121 : 0 : return false;
3122 : :
3123 : 36 : value_out.setObject(*obj);
3124 : :
3125 : 36 : return true;
3126 : 36 : }
3127 : :
3128 : 17 : bool gjs_array_from_basic_glist_gi_argument(JSContext* cx,
3129 : : JS::MutableHandleValue value_out,
3130 : : GITypeTag element_tag,
3131 : : GIArgument* arg) {
3132 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3133 : : "Converting GArgument glist to JS::Value");
3134 : 17 : return array_from_basic_linked_list(cx, value_out, element_tag,
3135 : 17 : gjs_arg_get<GList*>(arg));
3136 : : }
3137 : :
3138 : 19 : bool gjs_array_from_basic_gslist_gi_argument(JSContext* cx,
3139 : : JS::MutableHandleValue value_out,
3140 : : GITypeTag element_tag,
3141 : : GIArgument* arg) {
3142 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3143 : : "Converting GArgument gslist to JS::Value");
3144 : 19 : return array_from_basic_linked_list(cx, value_out, element_tag,
3145 : 19 : gjs_arg_get<GSList*>(arg));
3146 : : }
3147 : :
3148 : 25 : bool gjs_value_from_basic_ghash(JSContext* cx, JS::MutableHandleValue value_out,
3149 : : GITypeTag key_tag, GITypeTag value_tag,
3150 : : GHashTable* hash) {
3151 : 25 : g_assert(
3152 : : GI_TYPE_TAG_IS_BASIC(key_tag) &&
3153 : : "use gjs_object_from_g_hash() for hashes with non-basic key types");
3154 : 25 : g_assert(
3155 : : GI_TYPE_TAG_IS_BASIC(value_tag) &&
3156 : : "use gjs_object_from_g_hash() for hashes with non-basic value types");
3157 : :
3158 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3159 : : "Converting GIArgument ghash to JS::Value");
3160 : :
3161 : : // a NULL hash table becomes a null JS value
3162 [ + + ]: 25 : if (!hash) {
3163 : 5 : value_out.setNull();
3164 : 5 : return true;
3165 : : }
3166 : :
3167 : 20 : JS::RootedObject obj{cx, JS_NewPlainObject(cx)};
3168 [ - + ]: 20 : if (!obj)
3169 : 0 : return false;
3170 : :
3171 : 20 : JS::RootedValue v_key{cx}, v_val{cx};
3172 : 20 : JS::RootedId key{cx};
3173 : : GIArgument key_arg, value_arg;
3174 : : GHashTableIter iter;
3175 : : void* key_pointer;
3176 : : void* val_pointer;
3177 : 20 : g_hash_table_iter_init(&iter, hash);
3178 [ + + ]: 88 : while (g_hash_table_iter_next(&iter, &key_pointer, &val_pointer)) {
3179 : 68 : gi_type_tag_argument_from_hash_pointer(key_tag, key_pointer, &key_arg);
3180 : 68 : gi_type_tag_argument_from_hash_pointer(value_tag, val_pointer,
3181 : : &value_arg);
3182 : 68 : if (!gjs_value_from_basic_gi_argument(cx, &v_key, key_tag, &key_arg) ||
3183 [ + - ]: 68 : !JS_ValueToId(cx, v_key, &key) ||
3184 [ + - ]: 68 : !gjs_value_from_basic_gi_argument(cx, &v_val, value_tag,
3185 [ + - ]: 136 : &value_arg) ||
3186 [ - + - + ]: 136 : !JS_DefinePropertyById(cx, obj, key, v_val, JSPROP_ENUMERATE))
3187 : 0 : return false;
3188 : : }
3189 : :
3190 : 20 : value_out.setObject(*obj);
3191 : 20 : return true;
3192 : 20 : }
3193 : :
3194 : 30251 : bool gjs_value_from_gi_argument(JSContext* context,
3195 : : JS::MutableHandleValue value_p,
3196 : : GITypeInfo* type_info,
3197 : : GjsArgumentType argument_type,
3198 : : GITransfer transfer, GIArgument* arg) {
3199 : 30251 : GITypeTag type_tag = g_type_info_get_tag(type_info);
3200 : 30251 : bool is_pointer = g_type_info_is_pointer(type_info);
3201 [ + + ]: 30251 : if (Gjs::is_basic_type(type_tag, is_pointer)) {
3202 : 6424 : return gjs_value_from_basic_gi_argument(context, value_p, type_tag,
3203 : 6424 : arg);
3204 : : }
3205 : :
3206 [ + + ]: 23827 : if (type_tag == GI_TYPE_TAG_GLIST) {
3207 : 37 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3208 : 37 : GITypeTag element_tag = g_type_info_get_tag(element_type);
3209 : 37 : bool element_is_pointer = g_type_info_is_pointer(element_type);
3210 : :
3211 [ - + ]: 37 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
3212 : 0 : return gjs_array_from_basic_glist_gi_argument(context, value_p,
3213 : 0 : element_tag, arg);
3214 : : }
3215 : : // else fall through to generic marshaller
3216 [ + - ]: 37 : }
3217 : :
3218 [ + + ]: 23827 : if (type_tag == GI_TYPE_TAG_GSLIST) {
3219 : 39 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3220 : 39 : GITypeTag element_tag = g_type_info_get_tag(element_type);
3221 : 39 : bool element_is_pointer = g_type_info_is_pointer(element_type);
3222 : :
3223 [ - + ]: 39 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
3224 : 0 : return gjs_array_from_basic_gslist_gi_argument(context, value_p,
3225 : 0 : element_tag, arg);
3226 : : }
3227 : : // else fall through to generic marshaller
3228 [ + - ]: 39 : }
3229 : :
3230 [ + + ]: 23827 : if (type_tag == GI_TYPE_TAG_GHASH) {
3231 : 8 : GI::AutoTypeInfo key_type{g_type_info_get_param_type(type_info, 0)};
3232 : 8 : GITypeTag key_tag = g_type_info_get_tag(key_type);
3233 : 8 : bool key_is_pointer = g_type_info_is_pointer(key_type);
3234 : :
3235 : 8 : GI::AutoTypeInfo value_type{g_type_info_get_param_type(type_info, 1)};
3236 : 8 : GITypeTag value_tag = g_type_info_get_tag(value_type);
3237 : 8 : bool value_is_pointer = g_type_info_is_pointer(value_type);
3238 : :
3239 [ + - + + : 16 : if (Gjs::is_basic_type(key_tag, key_is_pointer) &&
+ + ]
3240 : 8 : Gjs::is_basic_type(value_tag, value_is_pointer)) {
3241 : 3 : return gjs_value_from_basic_ghash(context, value_p, key_tag,
3242 : : value_tag,
3243 : 3 : gjs_arg_get<GHashTable*>(arg));
3244 : : }
3245 : : // else fall through to generic marshaller
3246 [ + + + + ]: 11 : }
3247 : :
3248 [ + + ]: 23824 : if (type_tag == GI_TYPE_TAG_ARRAY) {
3249 : 532 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3250 : 532 : GITypeTag element_tag = g_type_info_get_tag(element_type);
3251 : 532 : bool element_is_pointer = g_type_info_is_pointer(element_type);
3252 : :
3253 [ + + ]: 532 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
3254 : 90 : GIArrayType array_type = g_type_info_get_array_type(type_info);
3255 : :
3256 [ + - - - : 90 : switch (array_type) {
- ]
3257 : 90 : case GI_ARRAY_TYPE_C: {
3258 [ + + ]: 90 : if (g_type_info_is_zero_terminated(type_info)) {
3259 : 86 : return gjs_array_from_basic_zero_terminated_array(
3260 : : context, value_p, element_tag,
3261 : 86 : gjs_arg_get<void*>(arg));
3262 : : }
3263 : :
3264 : : int fixed_size =
3265 : 4 : g_type_info_get_array_fixed_size(type_info);
3266 : 4 : g_assert(fixed_size != -1 &&
3267 : : "arrays with length param handled in "
3268 : : "gjs_value_from_basic_explicit_array()");
3269 : 4 : return gjs_value_from_basic_fixed_size_array_gi_argument(
3270 : 4 : context, value_p, element_tag, fixed_size, arg);
3271 : : }
3272 : :
3273 : 0 : case GI_ARRAY_TYPE_BYTE_ARRAY:
3274 : 0 : return gjs_value_from_byte_array_gi_argument(context,
3275 : 0 : value_p, arg);
3276 : :
3277 : 0 : case GI_ARRAY_TYPE_ARRAY:
3278 : 0 : return gjs_value_from_basic_garray_gi_argument(
3279 : 0 : context, value_p, element_tag, arg);
3280 : :
3281 : 0 : case GI_ARRAY_TYPE_PTR_ARRAY:
3282 : 0 : return gjs_value_from_basic_gptrarray_gi_argument(
3283 : 0 : context, value_p, element_tag, arg);
3284 : : }
3285 : : }
3286 : : // else fall through to generic marshaller
3287 [ + + ]: 532 : }
3288 : :
3289 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3290 : : "Converting GIArgument %s to JS::Value",
3291 : : g_type_tag_to_string(type_tag));
3292 : :
3293 [ + + + + : 23734 : switch (type_tag) {
+ + + - ]
3294 : 11 : case GI_TYPE_TAG_VOID:
3295 : 11 : g_assert(is_pointer &&
3296 : : "non-pointers should be handled by "
3297 : : "gjs_value_from_basic_gi_argument()");
3298 : : // If the argument is a pointer, convert to null to match our
3299 : : // in handling.
3300 : 11 : value_p.setNull();
3301 : 11 : return true;
3302 : :
3303 : 16 : case GI_TYPE_TAG_ERROR: {
3304 : 16 : GError* ptr = gjs_arg_get<GError*>(arg);
3305 [ + + ]: 16 : if (!ptr) {
3306 : 3 : value_p.setNull();
3307 : 3 : return true;
3308 : : }
3309 : :
3310 : 13 : JSObject* obj = ErrorInstance::object_for_c_ptr(context, ptr);
3311 [ - + ]: 13 : if (!obj)
3312 : 0 : return false;
3313 : :
3314 : 13 : value_p.setObject(*obj);
3315 : 13 : return true;
3316 : : }
3317 : :
3318 : 23184 : case GI_TYPE_TAG_INTERFACE:
3319 : : {
3320 : : GI::AutoBaseInfo interface_info{
3321 : 23184 : g_type_info_get_interface(type_info)};
3322 : 23184 : g_assert(interface_info);
3323 : :
3324 : 23184 : GIInfoType interface_type = interface_info.type();
3325 : :
3326 [ - + ]: 23184 : if (interface_type == GI_INFO_TYPE_UNRESOLVED) {
3327 : 0 : gjs_throw(context,
3328 : : "Unable to resolve arg type '%s'",
3329 : : g_base_info_get_name(interface_info));
3330 : 0 : return false;
3331 : : }
3332 : :
3333 : : // Enum/Flags are aren't pointer types, unlike the other interface
3334 : : // subtypes
3335 [ + + + + : 23184 : if (GI_IS_ENUM_INFO(interface_info)) {
+ + ]
3336 : 2326 : int64_t value_int64 = _gjs_enum_from_int(
3337 : : interface_info, gjs_arg_get<Gjs::Tag::Enum>(arg));
3338 : :
3339 [ + + ]: 2326 : if (interface_type == GI_INFO_TYPE_FLAGS) {
3340 : 318 : GType gtype = g_registered_type_info_get_g_type(
3341 : : interface_info.as<GIRegisteredTypeInfo>());
3342 : :
3343 [ + + ]: 318 : if (gtype != G_TYPE_NONE) {
3344 : : // Check to make sure 32 bit flag
3345 [ - + ]: 92 : if (static_cast<uint32_t>(value_int64) != value_int64) {
3346 : 0 : gjs_throw(context,
3347 : : "0x%" PRIx64
3348 : : " is not a valid value for flags %s",
3349 : : value_int64, g_type_name(gtype));
3350 : 0 : return false;
3351 : : }
3352 : :
3353 : : // Pass only valid values
3354 : 92 : Gjs::AutoTypeClass<GFlagsClass> gflags_class{gtype};
3355 : 92 : value_int64 &= gflags_class->mask;
3356 : 92 : }
3357 : : } else {
3358 [ - + ]: 2008 : if (!_gjs_enum_value_is_valid(context, interface_info,
3359 : : value_int64))
3360 : 0 : return false;
3361 : : }
3362 : :
3363 : 2326 : value_p.setNumber(static_cast<double>(value_int64));
3364 : 2326 : return true;
3365 : : }
3366 : :
3367 [ + + + + : 40159 : if (GI_IS_STRUCT_INFO(interface_info) &&
+ + ]
3368 : 19301 : g_struct_info_is_foreign(interface_info.as<GIStructInfo>())) {
3369 : 6 : return gjs_struct_foreign_convert_from_gi_argument(
3370 : 6 : context, value_p, interface_info, arg);
3371 : : }
3372 : :
3373 : : /* Everything else is a pointer type, NULL is the easy case */
3374 [ + + ]: 20852 : if (!gjs_arg_get<void*>(arg)) {
3375 : 192 : value_p.setNull();
3376 : 192 : return true;
3377 : : }
3378 : :
3379 [ + + - + : 39932 : if (GI_IS_STRUCT_INFO(interface_info) &&
- + ]
3380 : 19272 : g_struct_info_is_gtype_struct(
3381 : : interface_info.as<GIStructInfo>())) {
3382 : : /* XXX: here we make the implicit assumption that GTypeClass is the same
3383 : : as GTypeInterface. This is true for the GType field, which is what we
3384 : : use, but not for the rest of the structure!
3385 : : */
3386 : 0 : GType gtype = G_TYPE_FROM_CLASS(gjs_arg_get<GTypeClass*>(arg));
3387 : :
3388 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
# # ]
3389 : 0 : return gjs_lookup_interface_constructor(context, gtype,
3390 : 0 : value_p);
3391 : : }
3392 : 0 : return gjs_lookup_object_constructor(context, gtype, value_p);
3393 : : }
3394 : :
3395 : 20660 : GType gtype = g_registered_type_info_get_g_type(
3396 : : interface_info.as<GIRegisteredTypeInfo>());
3397 [ + + + + : 40147 : if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
+ + ]
3398 : 19487 : G_TYPE_IS_INTERFACE(gtype))
3399 : 1379 : gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
3400 : :
3401 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3402 : : "gtype of INTERFACE is %s", g_type_name(gtype));
3403 : :
3404 : :
3405 : : /* Test GValue and GError before Struct, or it will be handled as the latter */
3406 [ + + - + : 20660 : if (g_type_is_a(gtype, G_TYPE_VALUE)) {
+ + ]
3407 : 433 : return gjs_value_from_g_value(context, value_p,
3408 : 433 : gjs_arg_get<const GValue*>(arg));
3409 : : }
3410 : :
3411 [ + - - + : 20227 : if (g_type_is_a(gtype, G_TYPE_ERROR)) {
- + ]
3412 : 0 : JSObject* obj = ErrorInstance::object_for_c_ptr(
3413 : : context, gjs_arg_get<GError*>(arg));
3414 [ # # ]: 0 : if (!obj)
3415 : 0 : return false;
3416 : 0 : value_p.setObject(*obj);
3417 : 0 : return true;
3418 : : }
3419 : :
3420 [ + + - + : 20227 : if (GI_IS_STRUCT_INFO(interface_info) ||
+ + ]
3421 : : interface_type == GI_INFO_TYPE_BOXED) {
3422 [ + + ]: 18839 : if (arg::is_gdk_atom(interface_info)) {
3423 : : GI::AutoFunctionInfo atom_name_fun{
3424 : 1 : g_struct_info_find_method(interface_info, "name")};
3425 : : GIArgument atom_name_ret;
3426 : :
3427 : 1 : Gjs::AutoError error;
3428 [ - + ]: 1 : if (!g_function_info_invoke(atom_name_fun, arg, 1, nullptr,
3429 : : 0, &atom_name_ret,
3430 : : error.out())) {
3431 : 0 : gjs_throw(context, "Failed to call gdk_atom_name(): %s",
3432 : 0 : error->message);
3433 : 0 : return false;
3434 : : }
3435 : :
3436 : 1 : Gjs::AutoChar name{gjs_arg_get<char*>(&atom_name_ret)};
3437 [ - + ]: 1 : if (g_strcmp0("NONE", name) == 0) {
3438 : 0 : value_p.setNull();
3439 : 0 : return true;
3440 : : }
3441 : :
3442 : 1 : return gjs_string_from_utf8(context, name, value_p);
3443 : 1 : }
3444 : :
3445 : : JSObject *obj;
3446 : :
3447 [ + + ]: 18838 : if (gtype == G_TYPE_VARIANT) {
3448 : 3246 : transfer = GI_TRANSFER_EVERYTHING;
3449 [ + + ]: 15592 : } else if (transfer == GI_TRANSFER_CONTAINER) {
3450 [ + - ]: 8 : switch (argument_type) {
3451 : 8 : case GJS_ARGUMENT_ARRAY_ELEMENT:
3452 : : case GJS_ARGUMENT_LIST_ELEMENT:
3453 : : case GJS_ARGUMENT_HASH_ELEMENT:
3454 : 8 : transfer = GI_TRANSFER_EVERYTHING;
3455 : 8 : default:
3456 : 8 : break;
3457 : : }
3458 : : }
3459 : :
3460 [ + + ]: 18838 : if (transfer == GI_TRANSFER_EVERYTHING)
3461 : 18825 : obj = BoxedInstance::new_for_c_struct(
3462 : : context, interface_info, gjs_arg_get<void*>(arg));
3463 : : else
3464 : 13 : obj = BoxedInstance::new_for_c_struct(
3465 : : context, interface_info, gjs_arg_get<void*>(arg),
3466 : : BoxedInstance::NoCopy());
3467 : :
3468 [ - + ]: 18838 : if (!obj)
3469 : 0 : return false;
3470 : :
3471 : 18838 : value_p.setObject(*obj);
3472 : 18838 : return true;
3473 : : }
3474 : :
3475 [ + + ]: 1388 : if (GI_IS_UNION_INFO(interface_info)) {
3476 : 9 : JSObject* obj = UnionInstance::new_for_c_union(
3477 : : context, interface_info.as<GIUnionInfo>(),
3478 : : gjs_arg_get<void*>(arg));
3479 [ - + ]: 9 : if (!obj)
3480 : 0 : return false;
3481 : :
3482 : 9 : value_p.setObject(*obj);
3483 : 9 : return true;
3484 : : }
3485 : :
3486 [ + + + + : 1379 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
3487 : 1283 : g_assert(gjs_arg_get<void*>(arg) &&
3488 : : "Null arg is already handled above");
3489 : 1283 : return ObjectInstance::set_value_from_gobject(
3490 : 1283 : context, gjs_arg_get<GObject*>(arg), value_p);
3491 : : }
3492 : :
3493 [ + - + - ]: 96 : if (g_type_is_a(gtype, G_TYPE_BOXED) ||
3494 [ + - + - : 288 : g_type_is_a(gtype, G_TYPE_ENUM) ||
+ - - + ]
3495 [ - + ]: 96 : g_type_is_a(gtype, G_TYPE_FLAGS)) {
3496 : : /* Should have been handled above */
3497 : 0 : gjs_throw(context,
3498 : : "Type %s registered for unexpected interface_type %d",
3499 : : g_type_name(gtype),
3500 : : interface_type);
3501 : 0 : return false;
3502 : : }
3503 : :
3504 [ + - + + : 96 : if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
3505 : 92 : JSObject* obj = gjs_param_from_g_param(
3506 : 92 : context, G_PARAM_SPEC(gjs_arg_get<GParamSpec*>(arg)));
3507 [ - + ]: 92 : if (!obj)
3508 : 0 : return false;
3509 : 92 : value_p.setObject(*obj);
3510 : 92 : return true;
3511 : : }
3512 : :
3513 [ - + ]: 4 : if (gtype == G_TYPE_NONE) {
3514 : 0 : gjs_throw(context,
3515 : : "Unexpected unregistered type packing GIArgument "
3516 : : "into JS::Value");
3517 : 0 : return false;
3518 : : }
3519 : :
3520 [ - + - - : 4 : if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) {
+ - ]
3521 : 4 : JSObject* obj = FundamentalInstance::object_for_c_ptr(
3522 : : context, gjs_arg_get<void*>(arg));
3523 [ - + ]: 4 : if (!obj)
3524 : 0 : return false;
3525 : 4 : value_p.setObject(*obj);
3526 : 4 : return true;
3527 : : }
3528 : :
3529 : 0 : gjs_throw(context,
3530 : : "Unhandled GType %s packing GIArgument into JS::Value",
3531 : : g_type_name(gtype));
3532 : 0 : return false;
3533 : 23184 : }
3534 : :
3535 : 442 : case GI_TYPE_TAG_ARRAY:
3536 [ - + ]: 442 : if (!gjs_arg_get<void*>(arg)) {
3537 : 0 : value_p.setNull();
3538 : 0 : return true;
3539 : : }
3540 : :
3541 [ + + ]: 442 : if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
3542 [ + + ]: 440 : if (g_type_info_is_zero_terminated(type_info)) {
3543 : : GI::AutoTypeInfo param_info{
3544 : 426 : g_type_info_get_param_type(type_info, 0)};
3545 : 426 : g_assert(param_info != nullptr);
3546 : :
3547 : 426 : return gjs_array_from_zero_terminated_c_array(
3548 : : context, value_p, param_info, transfer,
3549 : 426 : gjs_arg_get<void*>(arg));
3550 : 426 : } else {
3551 : : /* arrays with length are handled outside of this function */
3552 : 14 : g_assert(((void) "Use gjs_value_from_explicit_array() for "
3553 : : "arrays with length param",
3554 : : g_type_info_get_array_length(type_info) == -1));
3555 : 14 : return gjs_array_from_fixed_size_array(context, value_p,
3556 : : type_info, transfer,
3557 : 14 : gjs_arg_get<void*>(arg));
3558 : : }
3559 [ - + ]: 2 : } else if (g_type_info_get_array_type(type_info) ==
3560 : : GI_ARRAY_TYPE_BYTE_ARRAY) {
3561 : 0 : auto* byte_array = gjs_arg_get<GByteArray*>(arg);
3562 : : JSObject* array =
3563 : 0 : gjs_byte_array_from_byte_array(context, byte_array);
3564 [ # # ]: 0 : if (!array) {
3565 : 0 : gjs_throw(context,
3566 : : "Couldn't convert GByteArray to a Uint8Array");
3567 : 0 : return false;
3568 : : }
3569 : 0 : value_p.setObject(*array);
3570 : : } else {
3571 : : // this assumes the array type is GArray or GPtrArray
3572 : : GI::AutoTypeInfo param_info{
3573 : 2 : g_type_info_get_param_type(type_info, 0)};
3574 : 2 : g_assert(param_info != nullptr);
3575 : :
3576 : 2 : return gjs_array_from_boxed_array(
3577 : : context, value_p, g_type_info_get_array_type(type_info),
3578 : 2 : param_info, transfer, arg);
3579 : 2 : }
3580 : 0 : break;
3581 : :
3582 : 37 : case GI_TYPE_TAG_GLIST:
3583 : 37 : return gjs_array_from_g_list(context, value_p, type_info, transfer,
3584 : 37 : gjs_arg_get<GList*>(arg));
3585 : 39 : case GI_TYPE_TAG_GSLIST:
3586 : 39 : return gjs_array_from_g_list(context, value_p, type_info, transfer,
3587 : 39 : gjs_arg_get<GSList*>(arg));
3588 : :
3589 : 5 : case GI_TYPE_TAG_GHASH:
3590 : : {
3591 : : GI::AutoTypeInfo key_param_info{
3592 : 5 : g_type_info_get_param_type(type_info, 0)};
3593 : : GI::AutoTypeInfo val_param_info{
3594 : 5 : g_type_info_get_param_type(type_info, 1)};
3595 : 5 : g_assert(key_param_info != nullptr);
3596 : 5 : g_assert(val_param_info != nullptr);
3597 : :
3598 : 5 : return gjs_object_from_g_hash(context, value_p, key_param_info,
3599 : : val_param_info, transfer,
3600 : 5 : gjs_arg_get<GHashTable*>(arg));
3601 : 5 : }
3602 : : break;
3603 : :
3604 : 0 : default:
3605 : : // basic types handled in gjs_value_from_basic_gi_argument()
3606 : 0 : g_warning("Unhandled type %s converting GIArgument to JavaScript",
3607 : : g_type_tag_to_string(type_tag));
3608 : 0 : return false;
3609 : : }
3610 : :
3611 : 0 : return true;
3612 : : }
3613 : :
3614 : : ///// RELEASE MARSHALLERS //////////////////////////////////////////////////////
3615 : : // These marshaller function are responsible for releasing the values stored in
3616 : : // GIArgument after a C function call succeeds or fails.
3617 : :
3618 : : template <typename T>
3619 : 75 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_g_arg_release_g_list(
3620 : : JSContext* cx, GITransfer transfer, GITypeInfo* type_info,
3621 : : GjsArgumentFlags flags, GIArgument* arg) {
3622 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
3623 : 75 : Gjs::SmartPointer<T> list{gjs_arg_steal<T*>(arg)};
3624 : :
3625 [ + + ]: 75 : if (transfer == GI_TRANSFER_CONTAINER)
3626 : 39 : return true;
3627 : :
3628 : : GIArgument elem;
3629 : 36 : GI::AutoTypeInfo param_info{g_type_info_get_param_type(type_info, 0)};
3630 : 36 : g_assert(param_info);
3631 : 36 : GITypeTag type_tag = g_type_info_get_tag(param_info);
3632 : :
3633 [ + + ]: 72 : for (T* l = list; l; l = l->next) {
3634 : 36 : gjs_arg_set(&elem, l->data);
3635 : :
3636 [ - + ]: 36 : if (!gjs_g_arg_release_internal(cx, transfer, param_info, type_tag,
3637 : : GJS_ARGUMENT_LIST_ELEMENT, flags,
3638 : : &elem)) {
3639 : 0 : return false;
3640 : : }
3641 : : }
3642 : :
3643 : 36 : return true;
3644 : 75 : }
3645 : :
3646 : : struct GHR_closure {
3647 : : JSContext *context;
3648 : : GI::AutoTypeInfo key_param_info, val_param_info;
3649 : : GITransfer transfer;
3650 : : GjsArgumentFlags flags;
3651 : : bool failed;
3652 : : };
3653 : :
3654 : : static gboolean
3655 : 7 : gjs_ghr_helper(gpointer key, gpointer val, gpointer user_data) {
3656 : 7 : GHR_closure *c = (GHR_closure *) user_data;
3657 : :
3658 : 7 : GITypeTag key_tag = g_type_info_get_tag(c->key_param_info);
3659 : 7 : GITypeTag val_type = g_type_info_get_tag(c->val_param_info);
3660 : 7 : g_assert(
3661 : : (!GI_TYPE_TAG_IS_BASIC(key_tag) || !GI_TYPE_TAG_IS_BASIC(val_type)) &&
3662 : : "use basic_ghash_release() instead");
3663 : :
3664 : : GIArgument key_arg, val_arg;
3665 : 7 : gjs_arg_set(&key_arg, key);
3666 : 7 : gjs_arg_set(&val_arg, val);
3667 [ - + ]: 7 : if (!gjs_g_arg_release_internal(c->context, c->transfer, c->key_param_info,
3668 : : key_tag, GJS_ARGUMENT_HASH_ELEMENT,
3669 : : c->flags, &key_arg))
3670 : 0 : c->failed = true;
3671 : :
3672 [ - + ]: 7 : switch (val_type) {
3673 : 0 : case GI_TYPE_TAG_DOUBLE:
3674 : : case GI_TYPE_TAG_FLOAT:
3675 : : case GI_TYPE_TAG_INT64:
3676 : : case GI_TYPE_TAG_UINT64:
3677 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<void*>(&val_arg), g_free);
3678 : 0 : break;
3679 : :
3680 : 7 : default:
3681 [ - + ]: 7 : if (!gjs_g_arg_release_internal(
3682 : : c->context, c->transfer, c->val_param_info, val_type,
3683 : : GJS_ARGUMENT_HASH_ELEMENT, c->flags, &val_arg))
3684 : 0 : c->failed = true;
3685 : : }
3686 : :
3687 : 7 : return true;
3688 : : }
3689 : :
3690 : : enum class ArrayReleaseType {
3691 : : EXPLICIT_LENGTH,
3692 : : ZERO_TERMINATED,
3693 : : };
3694 : :
3695 : : template <ArrayReleaseType release_type>
3696 : 0 : static inline void release_basic_array_internal(GITypeTag element_tag,
3697 : : unsigned length, void** array) {
3698 [ # # ]: 0 : if (!Gjs::basic_type_needs_release(element_tag))
3699 : 0 : return;
3700 : :
3701 : 0 : for (size_t ix = 0;; ix++) {
3702 : 0 : if constexpr (release_type == ArrayReleaseType::ZERO_TERMINATED) {
3703 [ # # ]: 0 : if (!array[ix])
3704 : 0 : break;
3705 : : }
3706 : : if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
3707 [ # # ]: 0 : if (ix == length)
3708 : 0 : break;
3709 : : }
3710 : :
3711 : 0 : g_free(array[ix]);
3712 : : }
3713 : : }
3714 : :
3715 : : template <ArrayReleaseType release_type = ArrayReleaseType::EXPLICIT_LENGTH>
3716 : 268 : static inline bool gjs_gi_argument_release_array_internal(
3717 : : JSContext* cx, GITransfer element_transfer, GjsArgumentFlags flags,
3718 : : GITypeInfo* param_type, unsigned length, GIArgument* arg) {
3719 : 533 : Gjs::AutoPointer<uint8_t, void, g_free> arg_array{
3720 : : gjs_arg_steal<uint8_t*>(arg)};
3721 : :
3722 [ + + ]: 268 : if (!arg_array)
3723 : 3 : return true;
3724 : :
3725 [ + + ]: 265 : if (element_transfer != GI_TRANSFER_EVERYTHING)
3726 : 8 : return true;
3727 : :
3728 : 257 : GITypeTag type_tag = g_type_info_get_tag(param_type);
3729 : 257 : bool is_pointer = g_type_info_is_pointer(param_type);
3730 [ - + ]: 257 : if (Gjs::is_basic_type(type_tag, is_pointer)) {
3731 : 0 : release_basic_array_internal<release_type>(type_tag, length,
3732 : : arg_array.as<void*>());
3733 : 0 : return true;
3734 : : }
3735 : :
3736 : : if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
3737 [ + + ]: 249 : if (length == 0)
3738 : 31 : return true;
3739 : : }
3740 : :
3741 [ + + + + ]: 438 : if (flags & GjsArgumentFlags::ARG_IN &&
3742 [ + + ]: 212 : !type_needs_release(param_type, type_tag))
3743 : 203 : return true;
3744 : :
3745 [ + + + + ]: 37 : if (flags & GjsArgumentFlags::ARG_OUT &&
3746 [ + + ]: 14 : !type_needs_out_release(param_type, type_tag))
3747 : 1 : return true;
3748 : :
3749 : 22 : size_t element_size = gjs_type_get_element_size(type_tag, param_type);
3750 [ - + ]: 22 : if G_UNLIKELY (element_size == 0)
3751 : 0 : return true;
3752 : :
3753 [ + - + ]: 139 : for (size_t i = 0;; i++) {
3754 : : GIArgument elem;
3755 : 73 : auto* element_start = &arg_array[i * element_size];
3756 : 73 : auto* pointer =
3757 [ + + ]: 73 : is_pointer ? *reinterpret_cast<uint8_t**>(element_start) : nullptr;
3758 : :
3759 : : if constexpr (release_type == ArrayReleaseType::ZERO_TERMINATED) {
3760 [ + - ]: 28 : if (is_pointer) {
3761 [ + + ]: 28 : if (!pointer)
3762 : 7 : break;
3763 [ # # ]: 0 : } else if (*element_start == 0 &&
3764 [ # # ]: 0 : memcmp(element_start, element_start + 1,
3765 : : element_size - 1) == 0) {
3766 : 0 : break;
3767 : : }
3768 : : }
3769 : :
3770 [ + + ]: 66 : gjs_arg_set(&elem, is_pointer ? pointer : element_start);
3771 : 66 : JS::AutoSaveExceptionState saved_exc(cx);
3772 [ - + ]: 66 : if (!gjs_g_arg_release_internal(cx, element_transfer, param_type,
3773 : : type_tag, GJS_ARGUMENT_ARRAY_ELEMENT,
3774 : : flags, &elem)) {
3775 : 0 : return false;
3776 : : }
3777 : :
3778 : : if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
3779 [ + + ]: 45 : if (i == length - 1)
3780 : 15 : break;
3781 : : }
3782 : : }
3783 : :
3784 : 22 : return true;
3785 : 268 : }
3786 : :
3787 : : /* We need to handle GI_TRANSFER_NOTHING differently for out parameters
3788 : : * (free nothing) and for in parameters (free any temporaries we've
3789 : : * allocated
3790 : : */
3791 : 23267 : constexpr static bool is_transfer_in_nothing(GITransfer transfer,
3792 : : GjsArgumentFlags flags) {
3793 [ + + + + ]: 23267 : return (transfer == GI_TRANSFER_NOTHING) && (flags & GjsArgumentFlags::ARG_IN);
3794 : : }
3795 : :
3796 : 325 : static void release_basic_type_internal(GITypeTag type_tag, GIArgument* arg) {
3797 [ + + ]: 325 : if (is_string_type(type_tag))
3798 [ + + ]: 106 : g_clear_pointer(&gjs_arg_member<char*>(arg), g_free);
3799 : 325 : }
3800 : :
3801 : : template <typename T>
3802 : 0 : static void basic_linked_list_release(GITransfer transfer,
3803 : : GITypeTag element_tag, GIArgument* arg) {
3804 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
3805 : 0 : g_assert(GI_TYPE_TAG_IS_BASIC(element_tag) &&
3806 : : "use gjs_g_arg_release_g_list() for lists with non-basic types");
3807 : :
3808 : 0 : Gjs::SmartPointer<T> list = gjs_arg_steal<T*>(arg);
3809 : :
3810 [ # # ]: 0 : if (transfer == GI_TRANSFER_CONTAINER)
3811 : 0 : return;
3812 : :
3813 : : GIArgument elem;
3814 [ # # ]: 0 : for (T* l = list; l; l = l->next) {
3815 : 0 : gjs_arg_set(&elem, l->data);
3816 : 0 : release_basic_type_internal(element_tag, &elem);
3817 : : }
3818 [ # # ]: 0 : }
3819 : :
3820 : 0 : void gjs_gi_argument_release_basic_glist(GITransfer transfer,
3821 : : GITypeTag element_tag,
3822 : : GIArgument* arg) {
3823 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GIArgument GList");
3824 : 0 : basic_linked_list_release<GList>(transfer, element_tag, arg);
3825 : 0 : }
3826 : :
3827 : 0 : void gjs_gi_argument_release_basic_gslist(GITransfer transfer,
3828 : : GITypeTag element_tag,
3829 : : GIArgument* arg) {
3830 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GIArgument GSList");
3831 : 0 : basic_linked_list_release<GSList>(transfer, element_tag, arg);
3832 : 0 : }
3833 : :
3834 : 27 : void gjs_gi_argument_release_basic_ghash(GITransfer transfer, GITypeTag key_tag,
3835 : : GITypeTag value_tag, GIArgument* arg) {
3836 : 27 : g_assert(GI_TYPE_TAG_IS_BASIC(key_tag) && GI_TYPE_TAG_IS_BASIC(value_tag));
3837 : :
3838 [ + + ]: 27 : if (!gjs_arg_get<GHashTable*>(arg))
3839 : 4 : return;
3840 : :
3841 : : Gjs::AutoPointer<GHashTable, GHashTable, g_hash_table_destroy> hash_table{
3842 : 23 : gjs_arg_steal<GHashTable*>(arg)};
3843 [ + + ]: 23 : if (transfer == GI_TRANSFER_CONTAINER) {
3844 : 4 : g_hash_table_remove_all(hash_table);
3845 : : } else {
3846 : 19 : std::array<GITypeTag, 2> data{key_tag, value_tag};
3847 : 19 : g_hash_table_foreach_steal(
3848 : : hash_table,
3849 : 19 : [](void* key, void* val, void* user_data) -> gboolean {
3850 : 66 : auto* tags = static_cast<std::array<GITypeTag, 2>*>(user_data);
3851 : 66 : GITypeTag key_tag = (*tags)[0], value_tag = (*tags)[1];
3852 : : GIArgument key_arg, val_arg;
3853 : 66 : gjs_arg_set(&key_arg, key);
3854 : 66 : gjs_arg_set(&val_arg, val);
3855 : 66 : release_basic_type_internal(key_tag, &key_arg);
3856 : :
3857 [ + + ]: 66 : switch (value_tag) {
3858 : 16 : case GI_TYPE_TAG_DOUBLE:
3859 : : case GI_TYPE_TAG_FLOAT:
3860 : : case GI_TYPE_TAG_INT64:
3861 : : case GI_TYPE_TAG_UINT64:
3862 [ + - ]: 16 : g_clear_pointer(&gjs_arg_member<void*>(&val_arg),
3863 : : g_free);
3864 : 16 : break;
3865 : :
3866 : 50 : default:
3867 : 50 : release_basic_type_internal(value_tag, &val_arg);
3868 : : }
3869 : :
3870 : 66 : return true;
3871 : : },
3872 : : &data);
3873 : : }
3874 : 23 : }
3875 : :
3876 : 58 : void gjs_gi_argument_release_basic_c_array(GITransfer transfer,
3877 : : GITypeTag element_tag,
3878 : : GIArgument* arg) {
3879 [ + + ]: 58 : if (!gjs_arg_get<void*>(arg))
3880 : 1 : return;
3881 : :
3882 [ + + + - : 57 : if (is_string_type(element_tag) && transfer != GI_TRANSFER_CONTAINER)
+ + ]
3883 [ + - ]: 54 : g_clear_pointer(&gjs_arg_member<GStrv>(arg), g_strfreev);
3884 : : else
3885 [ + - ]: 3 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
3886 : : }
3887 : :
3888 : 0 : void gjs_gi_argument_release_basic_c_array(GITransfer transfer,
3889 : : GITypeTag element_tag, size_t length,
3890 : : GIArgument* arg) {
3891 [ # # ]: 0 : if (!gjs_arg_get<void*>(arg))
3892 : 0 : return;
3893 : :
3894 : 114 : Gjs::AutoPointer<void*, void, g_free> array{gjs_arg_steal<void**>(arg)};
3895 : :
3896 [ # # # # : 0 : if (!is_string_type(element_tag) || transfer == GI_TRANSFER_CONTAINER)
# # ]
3897 : 0 : return;
3898 : :
3899 [ # # ]: 0 : for (size_t ix = 0; ix < length; ix++)
3900 : 0 : g_free(array[ix]);
3901 [ # # ]: 0 : }
3902 : :
3903 : 0 : void gjs_gi_argument_release_basic_garray(GITransfer transfer,
3904 : : GITypeTag element_tag,
3905 : : GIArgument* arg) {
3906 [ # # ]: 0 : if (!gjs_arg_get<void*>(arg))
3907 : 0 : return;
3908 : :
3909 : 1 : Gjs::AutoPointer<GArray, GArray, g_array_unref> array{
3910 : 0 : gjs_arg_steal<GArray*>(arg)};
3911 : :
3912 [ # # # # : 0 : if (transfer == GI_TRANSFER_CONTAINER || !is_string_type(element_tag))
# # ]
3913 : 0 : return;
3914 : :
3915 [ # # ]: 0 : for (size_t ix = 0; ix < array->len; ix++)
3916 : 0 : g_free(g_array_index(array, char*, ix));
3917 [ # # ]: 0 : }
3918 : :
3919 : 7 : void gjs_gi_argument_release_byte_array(GIArgument* arg) {
3920 [ - + ]: 7 : if (!gjs_arg_get<void*>(arg))
3921 : 0 : return;
3922 : :
3923 [ + - ]: 7 : g_clear_pointer(&gjs_arg_member<GByteArray*>(arg), g_byte_array_unref);
3924 : : }
3925 : :
3926 : 2 : void gjs_gi_argument_release_basic_gptrarray(GITransfer transfer,
3927 : : GITypeTag element_tag,
3928 : : GIArgument* arg) {
3929 [ - + ]: 2 : if (!gjs_arg_get<void*>(arg))
3930 : 1 : return;
3931 : :
3932 : 5 : Gjs::AutoPointer<GPtrArray, GPtrArray, g_ptr_array_unref> array{
3933 : 2 : gjs_arg_steal<GPtrArray*>(arg)};
3934 : :
3935 [ + + - + : 2 : if (transfer == GI_TRANSFER_CONTAINER || !is_string_type(element_tag))
+ + ]
3936 : 1 : return;
3937 : :
3938 : 1 : g_ptr_array_foreach(
3939 : 3 : array, [](void* ptr, void*) { g_free(ptr); }, nullptr);
3940 [ + + ]: 2 : }
3941 : :
3942 : : GJS_JSAPI_RETURN_CONVENTION
3943 : 17787 : static bool gjs_g_arg_release_internal(
3944 : : JSContext* context, GITransfer transfer, GITypeInfo* type_info,
3945 : : GITypeTag type_tag, [[maybe_unused]] GjsArgumentType argument_type,
3946 : : GjsArgumentFlags flags, GIArgument* arg) {
3947 : 17787 : g_assert(transfer != GI_TRANSFER_NOTHING ||
3948 : : flags != GjsArgumentFlags::NONE);
3949 : :
3950 : 17787 : bool is_pointer = g_type_info_is_pointer(type_info);
3951 [ + + ]: 17787 : if (Gjs::is_basic_type(type_tag, is_pointer)) {
3952 : 183 : release_basic_type_internal(type_tag, arg);
3953 : 183 : return true;
3954 : : }
3955 : :
3956 [ + + ]: 17604 : if (type_tag == GI_TYPE_TAG_GLIST) {
3957 : 36 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3958 : 36 : GITypeTag element_tag = g_type_info_get_tag(element_type);
3959 : 36 : bool element_is_pointer = g_type_info_is_pointer(element_type);
3960 : :
3961 [ - + ]: 36 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
3962 : 0 : basic_linked_list_release<GList>(transfer, element_tag, arg);
3963 : 0 : return true;
3964 : : }
3965 : : // else fall through to generic marshaller
3966 [ + - ]: 36 : }
3967 : :
3968 [ + + ]: 17604 : if (type_tag == GI_TYPE_TAG_GSLIST) {
3969 : 39 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3970 : 39 : GITypeTag element_tag = g_type_info_get_tag(element_type);
3971 : 39 : bool element_is_pointer = g_type_info_is_pointer(element_type);
3972 : :
3973 [ - + ]: 39 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
3974 : 0 : basic_linked_list_release<GSList>(transfer, element_tag, arg);
3975 : 0 : return true;
3976 : : }
3977 : : // else fall through to generic marshaller
3978 [ + - ]: 39 : }
3979 : :
3980 [ + + ]: 17604 : if (type_tag == GI_TYPE_TAG_GHASH) {
3981 : 8 : GI::AutoTypeInfo key_type{g_type_info_get_param_type(type_info, 0)};
3982 : 8 : GITypeTag key_tag = g_type_info_get_tag(key_type);
3983 : 8 : bool key_is_pointer = g_type_info_is_pointer(key_type);
3984 : 8 : GI::AutoTypeInfo value_type{g_type_info_get_param_type(type_info, 1)};
3985 : 8 : GITypeTag value_tag = g_type_info_get_tag(value_type);
3986 : 8 : bool value_is_pointer = g_type_info_is_pointer(value_type);
3987 : :
3988 [ + - + + : 16 : if (Gjs::is_basic_type(key_tag, key_is_pointer) &&
+ + ]
3989 : 8 : Gjs::is_basic_type(value_tag, value_is_pointer)) {
3990 : 4 : gjs_gi_argument_release_basic_ghash(transfer, key_tag, value_tag,
3991 : : arg);
3992 : 4 : return true;
3993 : : }
3994 : : // else fall through to generic marshaller
3995 [ + + + + ]: 12 : }
3996 : :
3997 [ + + ]: 17600 : if (type_tag == GI_TYPE_TAG_ARRAY) {
3998 : 79 : GI::AutoTypeInfo element_type{g_type_info_get_param_type(type_info, 0)};
3999 : 79 : GITypeTag element_tag = g_type_info_get_tag(element_type);
4000 : 79 : bool element_is_pointer = g_type_info_is_pointer(element_type);
4001 : :
4002 [ + + ]: 79 : if (Gjs::is_basic_type(element_tag, element_is_pointer)) {
4003 : 60 : GIArrayType array_type = g_type_info_get_array_type(type_info);
4004 [ + - - + : 60 : switch (array_type) {
- ]
4005 : 58 : case GI_ARRAY_TYPE_C:
4006 : 58 : gjs_gi_argument_release_basic_c_array(transfer, element_tag,
4007 : : arg);
4008 : 58 : return true;
4009 : 0 : case GI_ARRAY_TYPE_ARRAY:
4010 : 0 : gjs_gi_argument_release_basic_garray(transfer, element_tag,
4011 : : arg);
4012 : 0 : return true;
4013 : 0 : case GI_ARRAY_TYPE_BYTE_ARRAY:
4014 : 0 : gjs_gi_argument_release_byte_array(arg);
4015 : 0 : return true;
4016 : 2 : case GI_ARRAY_TYPE_PTR_ARRAY:
4017 : 2 : gjs_gi_argument_release_basic_gptrarray(transfer,
4018 : : element_tag, arg);
4019 : 2 : return true;
4020 : 0 : default:
4021 : : g_assert_not_reached();
4022 : : }
4023 : : }
4024 : : // else fall through to generic marshaller
4025 [ + + ]: 79 : }
4026 : :
4027 [ + + + + : 17540 : switch (type_tag) {
+ + + - ]
4028 : 8 : case GI_TYPE_TAG_VOID:
4029 : 8 : g_assert(is_pointer &&
4030 : : "non-pointer should be handled by "
4031 : : "release_basic_type_internal()");
4032 : 8 : break;
4033 : :
4034 : 12 : case GI_TYPE_TAG_ERROR:
4035 [ + - ]: 12 : if (!is_transfer_in_nothing(transfer, flags))
4036 : 12 : g_clear_error(&gjs_arg_member<GError*>(arg));
4037 : 12 : break;
4038 : :
4039 : 17422 : case GI_TYPE_TAG_INTERFACE:
4040 : : {
4041 : : GI::AutoBaseInfo interface_info{
4042 : 17422 : g_type_info_get_interface(type_info)};
4043 : 17422 : g_assert(interface_info);
4044 : :
4045 [ + + + + : 34267 : if (GI_IS_STRUCT_INFO(interface_info) &&
+ + ]
4046 : 16845 : g_struct_info_is_foreign(interface_info.as<GIStructInfo>()))
4047 : 4 : return gjs_struct_foreign_release_gi_argument(
4048 : 4 : context, transfer, interface_info, arg);
4049 : :
4050 [ + + + + : 17418 : if (GI_IS_ENUM_INFO(interface_info))
+ + ]
4051 : 39 : return true; // enum and flags
4052 : :
4053 : : /* Anything else is a pointer */
4054 [ + + ]: 17379 : if (!gjs_arg_get<void*>(arg))
4055 : 79 : return true;
4056 : :
4057 : 17300 : GType gtype = g_registered_type_info_get_g_type(
4058 : : interface_info.as<GIRegisteredTypeInfo>());
4059 [ + + + + : 34208 : if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
+ + ]
4060 : 16908 : G_TYPE_IS_INTERFACE(gtype))
4061 : 472 : gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
4062 : :
4063 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4064 : : "gtype of INTERFACE is %s", g_type_name(gtype));
4065 : :
4066 : : // In gjs_value_from_gi_argument we handle Struct/Union types
4067 : : // without a registered GType, but here we are specifically handling
4068 : : // a GIArgument that *owns* its value, and that is nonsensical for
4069 : : // such types, so we don't have to worry about it.
4070 : :
4071 [ + - + + : 17300 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
4072 [ + - ]: 367 : if (!is_transfer_in_nothing(transfer, flags))
4073 [ + - ]: 367 : g_clear_object(&gjs_arg_member<GObject*>(arg));
4074 [ + - + + : 16933 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
4075 [ + - ]: 82 : if (!is_transfer_in_nothing(transfer, flags))
4076 [ + - ]: 82 : g_clear_pointer(&gjs_arg_member<GParamSpec*>(arg),
4077 : : g_param_spec_unref);
4078 [ + - - + : 16851 : } else if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
- + ]
4079 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<GClosure*>(arg),
4080 : : g_closure_unref);
4081 [ + + - + : 16851 : } else if (g_type_is_a(gtype, G_TYPE_VALUE)) {
+ + ]
4082 : : /* G_TYPE_VALUE is-a G_TYPE_BOXED, but we special case it */
4083 [ + + ]: 66 : if (g_type_info_is_pointer (type_info))
4084 : 57 : g_boxed_free(gtype, gjs_arg_steal<void*>(arg));
4085 : : else
4086 [ + - ]: 9 : g_clear_pointer(&gjs_arg_member<GValue*>(arg),
4087 : : g_value_unset);
4088 [ + - + + : 16785 : } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + ]
4089 [ + - ]: 14948 : if (!is_transfer_in_nothing(transfer, flags))
4090 : 14948 : g_boxed_free(gtype, gjs_arg_steal<void*>(arg));
4091 [ + + - + : 1837 : } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) {
+ + ]
4092 [ + - ]: 1814 : if (!is_transfer_in_nothing(transfer, flags))
4093 [ + - ]: 1814 : g_clear_pointer(&gjs_arg_member<GVariant*>(arg),
4094 : : g_variant_unref);
4095 [ - + ]: 23 : } else if (gtype == G_TYPE_NONE) {
4096 [ # # ]: 0 : if (!is_transfer_in_nothing(transfer, flags)) {
4097 : 0 : gjs_throw(context,
4098 : : "Don't know how to release GIArgument: not an "
4099 : : "object or boxed type");
4100 : 0 : return false;
4101 : : }
4102 [ + - ]: 23 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
4103 [ + - ]: 23 : if (!is_transfer_in_nothing(transfer, flags)) {
4104 : : auto* priv =
4105 : 23 : FundamentalPrototype::for_gtype(context, gtype);
4106 : 23 : priv->call_unref_function(gjs_arg_steal<void*>(arg));
4107 : : }
4108 : : } else {
4109 : 0 : gjs_throw(context, "Unhandled GType %s releasing GIArgument",
4110 : : g_type_name(gtype));
4111 : 0 : return false;
4112 : : }
4113 : 17300 : return true;
4114 : 17422 : }
4115 : :
4116 : 19 : case GI_TYPE_TAG_ARRAY:
4117 : : {
4118 : 19 : GIArrayType array_type = g_type_info_get_array_type(type_info);
4119 : :
4120 [ + - ]: 19 : if (!gjs_arg_get<void*>(arg)) {
4121 : : /* OK */
4122 [ + + ]: 19 : } else if (array_type == GI_ARRAY_TYPE_C) {
4123 : : GI::AutoTypeInfo param_info{
4124 : 15 : g_type_info_get_param_type(type_info, 0)};
4125 : : GITypeTag element_type;
4126 : :
4127 : 15 : element_type = g_type_info_get_tag(param_info);
4128 : :
4129 [ + + - ]: 15 : switch (element_type) {
4130 : 7 : case GI_TYPE_TAG_INTERFACE:
4131 [ + + ]: 7 : if (!g_type_info_is_pointer(param_info)) {
4132 : : GI::AutoBaseInfo interface_info{
4133 : 3 : g_type_info_get_interface(param_info)};
4134 [ - + - - : 3 : if (GI_IS_STRUCT_INFO(interface_info) ||
+ - ]
4135 : 0 : GI_IS_UNION_INFO(interface_info)) {
4136 [ + - ]: 3 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
4137 : 3 : break;
4138 : : }
4139 [ - + ]: 3 : }
4140 : : [[fallthrough]];
4141 : : case GI_TYPE_TAG_GLIST:
4142 : : case GI_TYPE_TAG_GSLIST:
4143 : : case GI_TYPE_TAG_ARRAY:
4144 : : case GI_TYPE_TAG_GHASH:
4145 : : case GI_TYPE_TAG_ERROR: {
4146 : 12 : GITransfer element_transfer = transfer;
4147 : :
4148 [ - + - - ]: 12 : if (argument_type != GJS_ARGUMENT_ARGUMENT &&
4149 : : transfer != GI_TRANSFER_EVERYTHING)
4150 : 0 : element_transfer = GI_TRANSFER_NOTHING;
4151 : :
4152 [ + + ]: 12 : if (g_type_info_is_zero_terminated(type_info)) {
4153 : : return gjs_gi_argument_release_array_internal<
4154 : 14 : ArrayReleaseType::ZERO_TERMINATED>(
4155 : : context, element_transfer,
4156 : 14 : flags | GjsArgumentFlags::ARG_OUT, param_info, 0, arg);
4157 : : } else {
4158 : : return gjs_gi_argument_release_array_internal<
4159 : 10 : ArrayReleaseType::EXPLICIT_LENGTH>(
4160 : : context, element_transfer,
4161 : 10 : flags | GjsArgumentFlags::ARG_OUT, param_info,
4162 : 10 : g_type_info_get_array_fixed_size(type_info), arg);
4163 : : }
4164 : : }
4165 : :
4166 : 0 : default:
4167 : : // basic types handled above
4168 : 0 : gjs_throw(context,
4169 : : "Releasing a C array with explicit length, that was nested"
4170 : : "inside another container. This is not supported (and will leak)");
4171 : 0 : return false;
4172 : : }
4173 [ + + + + ]: 19 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
4174 : : GITypeTag element_type;
4175 : :
4176 : : GI::AutoTypeInfo param_info{
4177 : 1 : g_type_info_get_param_type(type_info, 0)};
4178 : 1 : element_type = g_type_info_get_tag(param_info);
4179 : :
4180 [ + - ]: 1 : switch (element_type) {
4181 : 1 : case GI_TYPE_TAG_ARRAY:
4182 : : case GI_TYPE_TAG_INTERFACE:
4183 : : case GI_TYPE_TAG_GLIST:
4184 : : case GI_TYPE_TAG_GSLIST:
4185 : : case GI_TYPE_TAG_GHASH:
4186 : : case GI_TYPE_TAG_ERROR: {
4187 : : Gjs::AutoPointer<GArray, GArray, g_array_unref> array{
4188 : 1 : gjs_arg_steal<GArray*>(arg)};
4189 : :
4190 [ + - - + : 2 : if (transfer != GI_TRANSFER_CONTAINER &&
- + ]
4191 : 1 : type_needs_out_release(param_info, element_type)) {
4192 : : guint i;
4193 : :
4194 [ # # ]: 0 : for (i = 0; i < array->len; i++) {
4195 : : GIArgument arg_iter;
4196 : :
4197 : 0 : gjs_arg_set(&arg_iter,
4198 : 0 : g_array_index(array, gpointer, i));
4199 [ # # ]: 0 : if (!gjs_g_arg_release_internal(
4200 : : context, transfer, param_info, element_type,
4201 : : GJS_ARGUMENT_ARRAY_ELEMENT, flags, &arg_iter))
4202 : 0 : return false;
4203 : : }
4204 : : }
4205 : :
4206 : 1 : break;
4207 [ - + ]: 1 : }
4208 : :
4209 : 0 : default:
4210 : : // basic types handled above
4211 : 0 : gjs_throw(context,
4212 : : "Don't know how to release GArray element-type %d",
4213 : : element_type);
4214 : 0 : return false;
4215 : : }
4216 : :
4217 [ + - + - ]: 4 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
4218 : : GI::AutoTypeInfo param_info{
4219 : 3 : g_type_info_get_param_type(type_info, 0)};
4220 : : Gjs::AutoPointer<GPtrArray, GPtrArray, g_ptr_array_unref> array{
4221 : 3 : gjs_arg_steal<GPtrArray*>(arg)};
4222 : :
4223 [ + + ]: 3 : if (transfer != GI_TRANSFER_CONTAINER) {
4224 : : guint i;
4225 : :
4226 [ + + ]: 8 : for (i = 0; i < array->len; i++) {
4227 : : GIArgument arg_iter;
4228 : :
4229 : 6 : gjs_arg_set(&arg_iter, g_ptr_array_index(array, i));
4230 [ - + ]: 6 : if (!gjs_gi_argument_release(context, transfer, param_info,
4231 : : flags, &arg_iter))
4232 : 0 : return false;
4233 : : }
4234 : : }
4235 [ + - + - ]: 3 : } else {
4236 : : // GI_ARRAY_TYPE_BYTEARRAY handled above; other values unknown
4237 : : g_assert_not_reached();
4238 : : }
4239 : 7 : break;
4240 : : }
4241 : :
4242 : 36 : case GI_TYPE_TAG_GLIST:
4243 : 36 : return gjs_g_arg_release_g_list<GList>(context, transfer, type_info,
4244 : 36 : flags, arg);
4245 : :
4246 : 39 : case GI_TYPE_TAG_GSLIST:
4247 : 39 : return gjs_g_arg_release_g_list<GSList>(context, transfer, type_info,
4248 : 39 : flags, arg);
4249 : :
4250 : 4 : case GI_TYPE_TAG_GHASH:
4251 [ + - ]: 4 : if (gjs_arg_get<GHashTable*>(arg)) {
4252 : : Gjs::AutoPointer<GHashTable, GHashTable, g_hash_table_destroy>
4253 : 4 : hash_table{gjs_arg_steal<GHashTable*>(arg)};
4254 [ - + ]: 4 : if (transfer == GI_TRANSFER_CONTAINER)
4255 : 0 : g_hash_table_remove_all(hash_table);
4256 : : else {
4257 : 4 : GHR_closure c = {context, nullptr, nullptr,
4258 : 4 : transfer, flags, false};
4259 : :
4260 : 4 : c.key_param_info = g_type_info_get_param_type(type_info, 0);
4261 : 4 : g_assert(c.key_param_info != nullptr);
4262 : 4 : c.val_param_info = g_type_info_get_param_type(type_info, 1);
4263 : 4 : g_assert(c.val_param_info != nullptr);
4264 : :
4265 : 4 : g_hash_table_foreach_steal(hash_table, gjs_ghr_helper, &c);
4266 : :
4267 [ - + ]: 4 : if (c.failed)
4268 : 0 : return false;
4269 [ + - ]: 4 : }
4270 [ + - ]: 4 : }
4271 : 4 : break;
4272 : :
4273 : 0 : default:
4274 : : // basic types should have been handled in release_basic_type_internal()
4275 : 0 : g_warning("Unhandled type %s releasing GIArgument",
4276 : : g_type_tag_to_string(type_tag));
4277 : 0 : return false;
4278 : : }
4279 : :
4280 : 31 : return true;
4281 : : }
4282 : :
4283 : 21959 : bool gjs_gi_argument_release(JSContext* cx, GITransfer transfer,
4284 : : GITypeInfo* type_info, GjsArgumentFlags flags,
4285 : : GIArgument* arg) {
4286 [ + + + + ]: 26460 : if (transfer == GI_TRANSFER_NOTHING &&
4287 [ + + ]: 4501 : !is_transfer_in_nothing(transfer, flags))
4288 : 4305 : return true;
4289 : :
4290 : 17654 : GITypeTag type_tag = g_type_info_get_tag(type_info);
4291 : :
4292 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4293 : : "Releasing GIArgument %s out param or return value",
4294 : : g_type_tag_to_string(type_tag));
4295 : :
4296 : 17654 : return gjs_g_arg_release_internal(cx, transfer, type_info, type_tag,
4297 : 17654 : GJS_ARGUMENT_ARGUMENT, flags, arg);
4298 : : }
4299 : :
4300 : 1542 : void gjs_gi_argument_release_basic(GITransfer transfer, GITypeTag type_tag,
4301 : : GjsArgumentFlags flags, GIArgument* arg) {
4302 [ + + + + ]: 3062 : if (transfer == GI_TRANSFER_NOTHING &&
4303 [ + + ]: 1520 : !is_transfer_in_nothing(transfer, flags))
4304 : 1516 : return;
4305 : :
4306 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4307 : : "Releasing GIArgument %s out param or return value",
4308 : : g_type_tag_to_string(type_tag));
4309 : :
4310 : 26 : release_basic_type_internal(type_tag, arg);
4311 : : }
4312 : :
4313 : 69 : bool gjs_gi_argument_release_in_arg(JSContext* cx, GITransfer transfer,
4314 : : GITypeInfo* type_info,
4315 : : GjsArgumentFlags flags, GIArgument* arg) {
4316 : : /* GI_TRANSFER_EVERYTHING: we don't own the argument anymore.
4317 : : * GI_TRANSFER_CONTAINER:
4318 : : * - non-containers: treated as GI_TRANSFER_EVERYTHING
4319 : : * - containers: See FIXME in gjs_array_to_g_list(); currently
4320 : : * an error and we won't get here.
4321 : : */
4322 [ + + ]: 69 : if (transfer != GI_TRANSFER_NOTHING)
4323 : 5 : return true;
4324 : :
4325 : 64 : GITypeTag type_tag = g_type_info_get_tag(type_info);
4326 : :
4327 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "Releasing GIArgument %s in param",
4328 : : g_type_tag_to_string(type_tag));
4329 : :
4330 [ + + ]: 64 : if (type_needs_release (type_info, type_tag))
4331 : 17 : return gjs_g_arg_release_internal(cx, transfer, type_info, type_tag,
4332 : 17 : GJS_ARGUMENT_ARGUMENT, flags, arg);
4333 : :
4334 : 47 : return true;
4335 : : }
4336 : :
4337 : 0 : void gjs_gi_argument_release_basic_in_array(GITransfer transfer,
4338 : : GITypeTag element_tag,
4339 : : GIArgument* arg) {
4340 [ # # ]: 0 : if (transfer != GI_TRANSFER_NOTHING)
4341 : 0 : return;
4342 : :
4343 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4344 : : "Releasing GIArgument basic C array in param");
4345 : :
4346 [ # # # # : 0 : if (is_string_type(element_tag) && transfer != GI_TRANSFER_CONTAINER)
# # ]
4347 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<GStrv>(arg), g_strfreev);
4348 : : else
4349 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
4350 : : }
4351 : :
4352 : 100 : void gjs_gi_argument_release_basic_in_array(GITransfer transfer,
4353 : : GITypeTag element_tag,
4354 : : size_t length, GIArgument* arg) {
4355 [ + + ]: 100 : if (transfer != GI_TRANSFER_NOTHING)
4356 : 80 : return;
4357 : :
4358 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4359 : : "Releasing GIArgument basic C array in param");
4360 : :
4361 : 98 : Gjs::AutoPointer<void*, void, g_free> array{gjs_arg_steal<void**>(arg)};
4362 : :
4363 [ + + ]: 98 : if (!is_string_type(element_tag))
4364 : 78 : return;
4365 : :
4366 [ + + ]: 70 : for (size_t ix = 0; ix < length; ix++)
4367 : 50 : g_free(array[ix]);
4368 [ + + ]: 98 : }
4369 : :
4370 : 250 : bool gjs_gi_argument_release_in_array(JSContext* context, GITransfer transfer,
4371 : : GITypeInfo* type_info, unsigned length,
4372 : : GIArgument* arg) {
4373 [ + + ]: 250 : if (transfer != GI_TRANSFER_NOTHING)
4374 : 7 : return true;
4375 : :
4376 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4377 : : "Releasing GIArgument array in param");
4378 : :
4379 : 243 : GI::AutoTypeInfo param_type{g_type_info_get_param_type(type_info, 0)};
4380 : : return gjs_gi_argument_release_array_internal<
4381 : 243 : ArrayReleaseType::EXPLICIT_LENGTH>(context, GI_TRANSFER_EVERYTHING,
4382 : : GjsArgumentFlags::ARG_IN, param_type,
4383 : 243 : length, arg);
4384 : 243 : }
4385 : :
4386 : 7 : bool gjs_gi_argument_release_in_array(JSContext* context, GITransfer transfer,
4387 : : GITypeInfo* type_info, GIArgument* arg) {
4388 [ + + ]: 7 : if (transfer != GI_TRANSFER_NOTHING)
4389 : 4 : return true;
4390 : :
4391 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4392 : : "Releasing GIArgument array in param");
4393 : :
4394 : 3 : GI::AutoTypeInfo param_type{g_type_info_get_param_type(type_info, 0)};
4395 : : return gjs_gi_argument_release_array_internal<
4396 : 3 : ArrayReleaseType::ZERO_TERMINATED>(context, GI_TRANSFER_EVERYTHING,
4397 : : GjsArgumentFlags::ARG_IN, param_type,
4398 : 3 : 0, arg);
4399 : 3 : }
4400 : :
4401 : 0 : void gjs_gi_argument_release_basic_out_array(GITransfer transfer,
4402 : : GITypeTag element_tag,
4403 : : GIArgument* arg) {
4404 [ # # ]: 0 : if (transfer == GI_TRANSFER_NOTHING)
4405 : 0 : return;
4406 : :
4407 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4408 : : "Releasing GIArgument array out param");
4409 : :
4410 [ # # # # : 0 : if (is_string_type(element_tag) && transfer != GI_TRANSFER_CONTAINER)
# # ]
4411 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<GStrv>(arg), g_strfreev);
4412 : : else
4413 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
4414 : : }
4415 : :
4416 : 49 : void gjs_gi_argument_release_basic_out_array(GITransfer transfer,
4417 : : GITypeTag element_tag,
4418 : : size_t length, GIArgument* arg) {
4419 [ + + ]: 49 : if (transfer == GI_TRANSFER_NOTHING)
4420 : 30 : return;
4421 : :
4422 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4423 : : "Releasing GIArgument array out param");
4424 : :
4425 : 30 : Gjs::AutoPointer<void*, void, g_free> array{gjs_arg_steal<void**>(arg)};
4426 : :
4427 [ + + + + : 30 : if (transfer == GI_TRANSFER_CONTAINER || !is_string_type(element_tag))
+ + ]
4428 : 11 : return;
4429 : :
4430 [ + + ]: 45 : for (size_t ix = 0; ix < length; ix++)
4431 : 26 : g_free(array[ix]);
4432 [ + + ]: 30 : }
4433 : :
4434 : 14 : bool gjs_gi_argument_release_out_array(JSContext* context, GITransfer transfer,
4435 : : GITypeInfo* type_info, unsigned length,
4436 : : GIArgument* arg) {
4437 [ + + ]: 14 : if (transfer == GI_TRANSFER_NOTHING)
4438 : 5 : return true;
4439 : :
4440 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4441 : : "Releasing GIArgument array out param");
4442 : :
4443 : 9 : GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
4444 [ + + ]: 9 : ? GI_TRANSFER_NOTHING
4445 : : : GI_TRANSFER_EVERYTHING;
4446 : :
4447 : 9 : GI::AutoTypeInfo param_type{g_type_info_get_param_type(type_info, 0)};
4448 : : return gjs_gi_argument_release_array_internal<
4449 : 9 : ArrayReleaseType::EXPLICIT_LENGTH>(context, element_transfer,
4450 : : GjsArgumentFlags::ARG_OUT,
4451 : 9 : param_type, length, arg);
4452 : 9 : }
4453 : :
4454 : 2 : bool gjs_gi_argument_release_out_array(JSContext* context, GITransfer transfer,
4455 : : GITypeInfo* type_info, GIArgument* arg) {
4456 [ + + ]: 2 : if (transfer == GI_TRANSFER_NOTHING)
4457 : 1 : return true;
4458 : :
4459 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
4460 : : "Releasing GIArgument array out param");
4461 : :
4462 : 1 : GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
4463 [ - + ]: 1 : ? GI_TRANSFER_NOTHING
4464 : : : GI_TRANSFER_EVERYTHING;
4465 : :
4466 : 1 : GI::AutoTypeInfo param_type{g_type_info_get_param_type(type_info, 0)};
4467 : : return gjs_gi_argument_release_array_internal<
4468 : 1 : ArrayReleaseType::ZERO_TERMINATED>(context, element_transfer,
4469 : : GjsArgumentFlags::ARG_OUT,
4470 : 1 : param_type, 0, arg);
4471 : 1 : }
|