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 <stdint.h>
9 : : #include <string.h> // for strcmp, strlen, memcpy
10 : :
11 : : #include <string>
12 : : #include <type_traits>
13 : :
14 : : #include <girepository.h>
15 : : #include <glib-object.h>
16 : : #include <glib.h>
17 : :
18 : : #include <js/Array.h>
19 : : #include <js/CharacterEncoding.h>
20 : : #include <js/Conversions.h>
21 : : #include <js/ErrorReport.h> // for JS_ReportOutOfMemory
22 : : #include <js/Exception.h>
23 : : #include <js/GCVector.h> // for RootedVector, MutableWrappedPtrOp...
24 : : #include <js/Id.h>
25 : : #include <js/PropertyAndElement.h> // for JS_GetElement, JS_HasPropertyById
26 : : #include <js/PropertyDescriptor.h> // for JSPROP_ENUMERATE
27 : : #include <js/RootingAPI.h>
28 : : #include <js/String.h>
29 : : #include <js/TypeDecls.h>
30 : : #include <js/Utility.h> // for UniqueChars
31 : : #include <js/Value.h>
32 : : #include <js/ValueArray.h>
33 : : #include <js/experimental/TypedData.h>
34 : : #include <jsapi.h> // for InformalValueTypeName, IdVector
35 : :
36 : : #include "gi/arg-inl.h"
37 : : #include "gi/arg-types-inl.h"
38 : : #include "gi/arg.h"
39 : : #include "gi/boxed.h"
40 : : #include "gi/closure.h"
41 : : #include "gi/foreign.h"
42 : : #include "gi/fundamental.h"
43 : : #include "gi/gerror.h"
44 : : #include "gi/gtype.h"
45 : : #include "gi/interface.h"
46 : : #include "gi/js-value-inl.h"
47 : : #include "gi/object.h"
48 : : #include "gi/param.h"
49 : : #include "gi/union.h"
50 : : #include "gi/value.h"
51 : : #include "gi/wrapperutils.h"
52 : : #include "gjs/atoms.h"
53 : : #include "gjs/byteArray.h"
54 : : #include "gjs/context-private.h"
55 : : #include "gjs/enum-utils.h"
56 : : #include "gjs/macros.h"
57 : : #include "gjs/jsapi-util.h"
58 : : #include "util/log.h"
59 : : #include "util/misc.h"
60 : :
61 : : GJS_JSAPI_RETURN_CONVENTION static bool gjs_g_arg_release_internal(
62 : : JSContext*, GITransfer, GITypeInfo*, GITypeTag, GjsArgumentType,
63 : : GjsArgumentFlags, GIArgument*);
64 : : static void throw_invalid_argument(JSContext* cx, JS::HandleValue value,
65 : : GITypeInfo* arginfo, const char* arg_name,
66 : : GjsArgumentType arg_type);
67 : :
68 : 17 : bool _gjs_flags_value_is_valid(JSContext* context, GType gtype, int64_t value) {
69 : : /* Do proper value check for flags with GType's */
70 [ + + ]: 17 : if (gtype != G_TYPE_NONE) {
71 : 16 : GjsAutoTypeClass<GFlagsClass> gflags_class(gtype);
72 : 16 : uint32_t tmpval = static_cast<uint32_t>(value);
73 : :
74 : : /* check all bits are valid bits for the flag and is a 32 bit flag*/
75 [ - + ]: 16 : if ((tmpval &= gflags_class->mask) != value) { /* Not a guint32 with invalid mask values*/
76 : 0 : gjs_throw(context,
77 : : "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s",
78 : : value, g_type_name(gtype));
79 : 0 : return false;
80 : : }
81 [ + - ]: 16 : }
82 : :
83 : 17 : return true;
84 : : }
85 : :
86 : : GJS_JSAPI_RETURN_CONVENTION
87 : 2100 : static bool _gjs_enum_value_is_valid(JSContext* context, GIEnumInfo* enum_info,
88 : : int64_t value) {
89 : : bool found;
90 : : int n_values;
91 : : int i;
92 : :
93 : 2100 : n_values = g_enum_info_get_n_values(enum_info);
94 : 2100 : found = false;
95 : :
96 [ + - ]: 28885 : for (i = 0; i < n_values; ++i) {
97 : 28885 : GjsAutoValueInfo value_info = g_enum_info_get_value(enum_info, i);
98 : 28885 : int64_t enum_value = g_value_info_get_value(value_info);
99 : :
100 [ + + ]: 28885 : if (enum_value == value) {
101 : 2100 : found = true;
102 : 2100 : break;
103 : : }
104 [ + + ]: 28885 : }
105 : :
106 [ - + ]: 2100 : if (!found) {
107 : 0 : gjs_throw(context,
108 : : "%" G_GINT64_MODIFIER "d is not a valid value for enumeration %s",
109 : : value, g_base_info_get_name((GIBaseInfo *)enum_info));
110 : : }
111 : :
112 : 2100 : return found;
113 : : }
114 : :
115 : 2306 : [[nodiscard]] static bool _gjs_enum_uses_signed_type(GIEnumInfo* enum_info) {
116 [ + + ]: 2306 : switch (g_enum_info_get_storage_type(enum_info)) {
117 : 82 : case GI_TYPE_TAG_INT8:
118 : : case GI_TYPE_TAG_INT16:
119 : : case GI_TYPE_TAG_INT32:
120 : : case GI_TYPE_TAG_INT64:
121 : 82 : return true;
122 : 2224 : default:
123 : 2224 : return false;
124 : : }
125 : : }
126 : :
127 : : // This is hacky - g_function_info_invoke() and g_field_info_get/set_field()
128 : : // expect the enum value in gjs_arg_member<int>(arg) and depend on all flags and
129 : : // enumerations being passed on the stack in a 32-bit field. See FIXME comment
130 : : // in g_field_info_get_field(). The same assumption of enums cast to 32-bit
131 : : // signed integers is found in g_value_set_enum()/g_value_set_flags().
132 : 2306 : [[nodiscard]] int64_t _gjs_enum_from_int(GIEnumInfo* enum_info, int int_value) {
133 [ + + ]: 2306 : if (_gjs_enum_uses_signed_type (enum_info))
134 : 82 : return int64_t(int_value);
135 : : else
136 : 2224 : return int64_t(uint32_t(int_value));
137 : : }
138 : :
139 : : /* Here for symmetry, but result is the same for the two cases */
140 : 102 : [[nodiscard]] static int _gjs_enum_to_int(int64_t value) {
141 : 102 : return static_cast<int>(value);
142 : : }
143 : :
144 : : /* Check if an argument of the given needs to be released if we created it
145 : : * from a JS value to pass it into a function and aren't transferring ownership.
146 : : */
147 : 368 : [[nodiscard]] static bool type_needs_release(GITypeInfo* type_info,
148 : : GITypeTag type_tag) {
149 [ + + + ]: 368 : switch (type_tag) {
150 : 64 : case GI_TYPE_TAG_ARRAY:
151 : : case GI_TYPE_TAG_ERROR:
152 : : case GI_TYPE_TAG_FILENAME:
153 : : case GI_TYPE_TAG_GHASH:
154 : : case GI_TYPE_TAG_GLIST:
155 : : case GI_TYPE_TAG_GSLIST:
156 : : case GI_TYPE_TAG_UTF8:
157 : 64 : return true;
158 : :
159 : 203 : case GI_TYPE_TAG_INTERFACE: {
160 : : GType gtype;
161 : :
162 : : GjsAutoBaseInfo interface_info =
163 : 203 : g_type_info_get_interface(type_info);
164 : 203 : g_assert(interface_info != nullptr);
165 : :
166 [ + - - ]: 203 : switch (g_base_info_get_type(interface_info)) {
167 : 203 : case GI_INFO_TYPE_STRUCT:
168 : : case GI_INFO_TYPE_ENUM:
169 : : case GI_INFO_TYPE_FLAGS:
170 : : case GI_INFO_TYPE_OBJECT:
171 : : case GI_INFO_TYPE_INTERFACE:
172 : : case GI_INFO_TYPE_UNION:
173 : : case GI_INFO_TYPE_BOXED:
174 : : // These are subtypes of GIRegisteredTypeInfo for which the
175 : : // cast is safe
176 : 203 : gtype = g_registered_type_info_get_g_type(interface_info);
177 : 203 : break;
178 : 0 : case GI_INFO_TYPE_VALUE:
179 : : // Special case for GValues
180 : 0 : return true;
181 : 0 : default:
182 : 0 : gtype = G_TYPE_NONE;
183 : : }
184 : :
185 [ + - - + : 203 : if (g_type_is_a(gtype, G_TYPE_CLOSURE))
- + ]
186 : 0 : return true;
187 [ + + - + : 203 : else if (g_type_is_a(gtype, G_TYPE_VALUE))
+ + ]
188 : 3 : return true;
189 : : else
190 : 200 : return false;
191 : 203 : }
192 : :
193 : 101 : default:
194 : 101 : return false;
195 : : }
196 : : }
197 : :
198 : 11 : [[nodiscard]] static inline bool is_string_type(GITypeTag tag) {
199 [ + - + + ]: 11 : return tag == GI_TYPE_TAG_FILENAME || tag == GI_TYPE_TAG_UTF8;
200 : : }
201 : :
202 : : /* Check if an argument of the given needs to be released if we obtained it
203 : : * from out argument (or the return value), and we're transferring ownership
204 : : */
205 : 16 : [[nodiscard]] static bool type_needs_out_release(GITypeInfo* type_info,
206 : : GITypeTag type_tag) {
207 [ + + + ]: 16 : switch (type_tag) {
208 : 6 : case GI_TYPE_TAG_ARRAY:
209 : : case GI_TYPE_TAG_ERROR:
210 : : case GI_TYPE_TAG_FILENAME:
211 : : case GI_TYPE_TAG_GHASH:
212 : : case GI_TYPE_TAG_GLIST:
213 : : case GI_TYPE_TAG_GSLIST:
214 : : case GI_TYPE_TAG_UTF8:
215 : 6 : return true;
216 : :
217 : 4 : case GI_TYPE_TAG_INTERFACE: {
218 : : GjsAutoBaseInfo interface_info =
219 : 4 : g_type_info_get_interface(type_info);
220 : :
221 [ - + + - ]: 4 : switch (g_base_info_get_type(interface_info)) {
222 : 0 : case GI_INFO_TYPE_ENUM:
223 : : case GI_INFO_TYPE_FLAGS:
224 : 0 : return false;
225 : :
226 : 3 : case GI_INFO_TYPE_STRUCT:
227 : : case GI_INFO_TYPE_UNION:
228 : 3 : return g_type_info_is_pointer(type_info);
229 : :
230 : 1 : case GI_INFO_TYPE_OBJECT:
231 : 1 : return true;
232 : :
233 : 0 : default:
234 : 0 : return false;
235 : : }
236 : 4 : }
237 : :
238 : 6 : default:
239 : 6 : return false;
240 : : }
241 : : }
242 : :
243 : : template <typename T>
244 : 14 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_to_g_list(
245 : : JSContext* cx, const JS::HandleValue& value, GITypeInfo* type_info,
246 : : GITransfer transfer, const char* arg_name, GjsArgumentType arg_type,
247 : : T** list_p) {
248 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
249 : :
250 : : // While a list can be NULL in C, that means empty array in JavaScript, it
251 : : // doesn't mean null in JavaScript.
252 : : bool is_array;
253 [ - + ]: 14 : if (!JS::IsArrayObject(cx, value, &is_array))
254 : 0 : return false;
255 [ - + ]: 14 : if (!is_array) {
256 : 0 : throw_invalid_argument(cx, value, type_info, arg_name, arg_type);
257 : 0 : return false;
258 : : }
259 : :
260 : 14 : JS::RootedObject array_obj(cx, &value.toObject());
261 : :
262 : : uint32_t length;
263 [ - + ]: 14 : if (!JS::GetArrayLength(cx, array_obj, &length)) {
264 : 0 : throw_invalid_argument(cx, value, type_info, arg_name, arg_type);
265 : 0 : return false;
266 : : }
267 : :
268 : 14 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
269 : 14 : g_assert(param_info);
270 : :
271 [ + + ]: 14 : if (transfer == GI_TRANSFER_CONTAINER) {
272 [ - + ]: 1 : if (type_needs_release (param_info, g_type_info_get_tag(param_info))) {
273 : : /* FIXME: to make this work, we'd have to keep a list of temporary
274 : : * GArguments for the function call so we could free them after
275 : : * the surrounding container had been freed by the callee.
276 : : */
277 : 0 : gjs_throw(cx, "Container transfer for in parameters not supported");
278 : 0 : return false;
279 : : }
280 : :
281 : 1 : transfer = GI_TRANSFER_NOTHING;
282 : : }
283 : :
284 : 14 : JS::RootedObject array(cx, value.toObjectOrNull());
285 : 14 : JS::RootedValue elem(cx);
286 : 14 : T* list = nullptr;
287 : :
288 [ + + ]: 50 : for (size_t i = 0; i < length; ++i) {
289 : 36 : GArgument elem_arg = { 0 };
290 : :
291 : 36 : elem = JS::UndefinedValue();
292 [ - + ]: 36 : if (!JS_GetElement(cx, array, i, &elem)) {
293 : 0 : gjs_throw(cx, "Missing array element %zu", i);
294 : 0 : return false;
295 : : }
296 : :
297 : : /* FIXME we don't know if the list elements can be NULL.
298 : : * gobject-introspection needs to tell us this.
299 : : * Always say they can't for now.
300 : : */
301 [ - + ]: 36 : if (!gjs_value_to_g_argument(cx, elem, param_info,
302 : : GJS_ARGUMENT_LIST_ELEMENT, transfer,
303 : : &elem_arg)) {
304 : 0 : return false;
305 : : }
306 : :
307 : : void* hash_pointer =
308 : 36 : g_type_info_hash_pointer_from_argument(param_info, &elem_arg);
309 : :
310 : : if constexpr (std::is_same_v<T, GList>)
311 : 20 : list = g_list_prepend(list, hash_pointer);
312 : : else if constexpr (std::is_same_v<T, GSList>)
313 : 16 : list = g_slist_prepend(list, hash_pointer);
314 : : }
315 : :
316 : : if constexpr (std::is_same_v<T, GList>)
317 : 7 : list = g_list_reverse(list);
318 : : else if constexpr (std::is_same_v<T, GSList>)
319 : 7 : list = g_slist_reverse(list);
320 : :
321 : 14 : *list_p = list;
322 : :
323 : 14 : return true;
324 : 14 : }
325 : :
326 : 11 : [[nodiscard]] static GHashTable* create_hash_table_for_key_type(
327 : : GITypeTag key_type) {
328 : : /* Don't use key/value destructor functions here, because we can't
329 : : * construct correct ones in general if the value type is complex.
330 : : * Rely on the type-aware g_argument_release functions. */
331 [ + + ]: 11 : if (is_string_type(key_type))
332 : 9 : return g_hash_table_new(g_str_hash, g_str_equal);
333 : 2 : return g_hash_table_new(NULL, NULL);
334 : : }
335 : :
336 : : template <typename IntType>
337 : 8 : GJS_JSAPI_RETURN_CONVENTION static bool hashtable_int_key(
338 : : JSContext* cx, const JS::HandleValue& value, void** pointer_out) {
339 : : static_assert(std::is_integral_v<IntType>, "Need an integer");
340 : 8 : bool out_of_range = false;
341 : :
342 : : Gjs::JsValueHolder::Strict<IntType> i;
343 [ - + ]: 8 : if (!Gjs::js_value_to_c_checked<IntType>(cx, value, &i, &out_of_range))
344 : 0 : return false;
345 : :
346 [ - + ]: 8 : if (out_of_range) {
347 : 0 : gjs_throw(cx, "value is out of range for hash table key of type %s",
348 : : Gjs::static_type_name<IntType>());
349 : : }
350 : :
351 : 8 : *pointer_out = gjs_int_to_pointer<IntType>(i);
352 : :
353 : 8 : return true;
354 : : }
355 : :
356 : : /* Converts a JS::Value to a GHashTable key, stuffing it into @pointer_out if
357 : : * possible, otherwise giving the location of an allocated key in @pointer_out.
358 : : */
359 : : GJS_JSAPI_RETURN_CONVENTION
360 : 41 : static bool value_to_ghashtable_key(JSContext* cx, JS::HandleValue value,
361 : : GITypeTag type_tag, void** pointer_out) {
362 : 41 : bool unsupported = false;
363 : :
364 : 41 : g_assert((value.isString() || value.isInt32()) &&
365 : : "keys from JS_Enumerate must be non-symbol property keys");
366 : :
367 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
368 : : "Converting JS::Value to GHashTable key %s",
369 : : g_type_tag_to_string(type_tag));
370 : :
371 [ - - - - : 41 : switch (type_tag) {
+ - - - -
+ - - ]
372 : 0 : case GI_TYPE_TAG_BOOLEAN:
373 : : /* This doesn't seem particularly useful, but it's easy */
374 : 0 : *pointer_out = gjs_int_to_pointer(JS::ToBoolean(value));
375 : 0 : break;
376 : :
377 : 0 : case GI_TYPE_TAG_UNICHAR:
378 [ # # ]: 0 : if (value.isInt32()) {
379 : 0 : *pointer_out = gjs_int_to_pointer(value.toInt32());
380 : : } else {
381 : : uint32_t ch;
382 [ # # ]: 0 : if (!gjs_unichar_from_string(cx, value, &ch))
383 : 0 : return false;
384 : 0 : *pointer_out = gjs_int_to_pointer(ch);
385 : : }
386 : 0 : break;
387 : :
388 : 0 : case GI_TYPE_TAG_INT8:
389 [ # # ]: 0 : if (!hashtable_int_key<int8_t>(cx, value, pointer_out))
390 : 0 : return false;
391 : 0 : break;
392 : :
393 : 0 : case GI_TYPE_TAG_INT16:
394 [ # # ]: 0 : if (!hashtable_int_key<int16_t>(cx, value, pointer_out))
395 : 0 : return false;
396 : 0 : break;
397 : :
398 : 8 : case GI_TYPE_TAG_INT32:
399 [ - + ]: 8 : if (!hashtable_int_key<int32_t>(cx, value, pointer_out))
400 : 0 : return false;
401 : 8 : break;
402 : :
403 : 0 : case GI_TYPE_TAG_UINT8:
404 [ # # ]: 0 : if (!hashtable_int_key<uint8_t>(cx, value, pointer_out))
405 : 0 : return false;
406 : 0 : break;
407 : :
408 : 0 : case GI_TYPE_TAG_UINT16:
409 [ # # ]: 0 : if (!hashtable_int_key<uint16_t>(cx, value, pointer_out))
410 : 0 : return false;
411 : 0 : break;
412 : :
413 : 0 : case GI_TYPE_TAG_UINT32:
414 [ # # ]: 0 : if (!hashtable_int_key<uint32_t>(cx, value, pointer_out))
415 : 0 : return false;
416 : 0 : break;
417 : :
418 : 0 : case GI_TYPE_TAG_FILENAME: {
419 : 0 : GjsAutoChar cstr;
420 : 0 : JS::RootedValue str_val(cx, value);
421 [ # # ]: 0 : if (!str_val.isString()) {
422 : 0 : JS::RootedString str(cx, JS::ToString(cx, str_val));
423 : 0 : str_val.setString(str);
424 : 0 : }
425 [ # # ]: 0 : if (!gjs_string_to_filename(cx, str_val, &cstr))
426 : 0 : return false;
427 : 0 : *pointer_out = cstr.release();
428 : 0 : break;
429 [ # # # # ]: 0 : }
430 : :
431 : 33 : case GI_TYPE_TAG_UTF8: {
432 : 33 : JS::RootedString str(cx);
433 [ + + ]: 33 : if (!value.isString())
434 : 18 : str = JS::ToString(cx, value);
435 : : else
436 : 15 : str = value.toString();
437 : :
438 : 33 : JS::UniqueChars cstr(JS_EncodeStringToUTF8(cx, str));
439 [ - + ]: 33 : if (!cstr)
440 : 0 : return false;
441 : 33 : *pointer_out = g_strdup(cstr.get());
442 : 33 : break;
443 [ - + - + ]: 66 : }
444 : :
445 : 0 : case GI_TYPE_TAG_FLOAT:
446 : : case GI_TYPE_TAG_DOUBLE:
447 : : case GI_TYPE_TAG_INT64:
448 : : case GI_TYPE_TAG_UINT64:
449 : : /* FIXME: The above four could be supported, but are currently not. The ones
450 : : * below cannot be key types in a regular JS object; we would need to allow
451 : : * marshalling Map objects into GHashTables to support those. */
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 [ - + ]: 41 : 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 : 41 : return true;
477 : : }
478 : :
479 : : template <typename T>
480 : 16 : [[nodiscard]] static T* heap_value_new_from_arg(GIArgument* val_arg) {
481 : 16 : T* heap_val = g_new(T, 1);
482 : 16 : *heap_val = gjs_arg_get<T>(val_arg);
483 : :
484 : 16 : return heap_val;
485 : : }
486 : :
487 : : GJS_JSAPI_RETURN_CONVENTION
488 : 11 : static bool gjs_object_to_g_hash(JSContext* context, JS::HandleObject props,
489 : : GITypeInfo* type_info, GITransfer transfer,
490 : : GHashTable** hash_p) {
491 : : size_t id_ix, id_len;
492 : :
493 : 11 : g_assert(props && "Property bag cannot be null");
494 : :
495 : 11 : GjsAutoBaseInfo key_param_info = g_type_info_get_param_type(type_info, 0);
496 : 11 : GjsAutoBaseInfo val_param_info = g_type_info_get_param_type(type_info, 1);
497 : :
498 [ - + ]: 11 : if (transfer == GI_TRANSFER_CONTAINER) {
499 [ # # # # : 0 : if (type_needs_release (key_param_info, g_type_info_get_tag(key_param_info)) ||
# # ]
500 : 0 : type_needs_release (val_param_info, g_type_info_get_tag(val_param_info))) {
501 : : /* FIXME: to make this work, we'd have to keep a list of temporary
502 : : * GArguments for the function call so we could free them after
503 : : * the surrounding container had been freed by the callee.
504 : : */
505 : 0 : gjs_throw(context,
506 : : "Container transfer for in parameters not supported");
507 : 0 : return false;
508 : : }
509 : :
510 : 0 : transfer = GI_TRANSFER_NOTHING;
511 : : }
512 : :
513 : 11 : JS::Rooted<JS::IdVector> ids(context, context);
514 [ - + ]: 11 : if (!JS_Enumerate(context, props, &ids))
515 : 0 : return false;
516 : :
517 : 11 : GITypeTag key_tag = g_type_info_get_tag(key_param_info);
518 : : GjsAutoPointer<GHashTable, GHashTable, g_hash_table_destroy> result =
519 : 11 : create_hash_table_for_key_type(key_tag);
520 : :
521 : 11 : JS::RootedValue key_js(context), val_js(context);
522 : 11 : JS::RootedId cur_id(context);
523 [ + + ]: 52 : for (id_ix = 0, id_len = ids.length(); id_ix < id_len; ++id_ix) {
524 : 41 : cur_id = ids[id_ix];
525 : : gpointer key_ptr, val_ptr;
526 : 41 : GIArgument val_arg = { 0 };
527 : :
528 : 41 : if (!JS_IdToValue(context, cur_id, &key_js) ||
529 : : // Type check key type.
530 [ + - ]: 41 : !value_to_ghashtable_key(context, key_js, key_tag, &key_ptr) ||
531 [ + - + - ]: 123 : !JS_GetPropertyById(context, props, cur_id, &val_js) ||
532 : : // Type check and convert value to a C type
533 [ - + - + ]: 82 : !gjs_value_to_g_argument(context, val_js, val_param_info, nullptr,
534 : : GJS_ARGUMENT_HASH_ELEMENT, transfer,
535 : : GjsArgumentFlags::MAY_BE_NULL, &val_arg))
536 : 0 : return false;
537 : :
538 : 41 : GITypeTag val_type = g_type_info_get_tag(val_param_info);
539 : : /* Use heap-allocated values for types that don't fit in a pointer */
540 [ + + ]: 41 : if (val_type == GI_TYPE_TAG_INT64) {
541 : 4 : val_ptr = heap_value_new_from_arg<int64_t>(&val_arg);
542 [ + + ]: 37 : } else if (val_type == GI_TYPE_TAG_UINT64) {
543 : 4 : val_ptr = heap_value_new_from_arg<uint64_t>(&val_arg);
544 [ + + ]: 33 : } else if (val_type == GI_TYPE_TAG_FLOAT) {
545 : 4 : val_ptr = heap_value_new_from_arg<float>(&val_arg);
546 [ + + ]: 29 : } else if (val_type == GI_TYPE_TAG_DOUBLE) {
547 : 4 : val_ptr = heap_value_new_from_arg<double>(&val_arg);
548 : : } else {
549 : : // Other types are simply stuffed inside the pointer
550 : 25 : val_ptr = g_type_info_hash_pointer_from_argument(val_param_info,
551 : : &val_arg);
552 : : }
553 : :
554 : : #if __GNUC__ >= 8 // clang-format off
555 : : _Pragma("GCC diagnostic push")
556 : : _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
557 : : #endif
558 : : // The compiler isn't smart enough to figure out that key_ptr will
559 : : // always be initialized if value_to_ghashtable_key() returns true.
560 : 41 : g_hash_table_insert(result, key_ptr, val_ptr);
561 : : #if __GNUC__ >= 8
562 : : _Pragma("GCC diagnostic pop")
563 : : #endif // clang-format on
564 : : }
565 : :
566 : 11 : *hash_p = result.release();
567 : 11 : return true;
568 : 11 : }
569 : :
570 : : template <typename T>
571 : 325 : [[nodiscard]] constexpr T* array_allocate(size_t length) {
572 : : if constexpr (std::is_same_v<T, char*>)
573 : 39 : return g_new0(char*, length);
574 : :
575 : 286 : T* array = g_new(T, length);
576 : 286 : array[length - 1] = {0};
577 : 286 : return array;
578 : : }
579 : :
580 : : template <GITypeTag TAG, typename T>
581 : 347 : GJS_JSAPI_RETURN_CONVENTION static bool js_value_to_c_strict(
582 : : JSContext* cx, const JS::HandleValue& value, T* out) {
583 : : using ValueHolderT = Gjs::JsValueHolder::Strict<T, TAG>;
584 : : if constexpr (Gjs::type_has_js_getter<T, ValueHolderT>())
585 : 311 : return Gjs::js_value_to_c<TAG>(cx, value, out);
586 : :
587 : : ValueHolderT v;
588 : 36 : bool ret = Gjs::js_value_to_c<TAG>(cx, value, &v);
589 : 36 : *out = v;
590 : :
591 : 36 : return ret;
592 : : }
593 : :
594 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
595 : 101 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_to_auto_array(
596 : : JSContext* cx, JS::Value array_value, size_t length, void** arr_p) {
597 : 101 : JS::RootedObject array(cx, array_value.toObjectOrNull());
598 : 101 : JS::RootedValue elem(cx);
599 : :
600 : : // Add one so we're always zero terminated
601 : 101 : GjsSmartPointer<T> result = array_allocate<T>(length + 1);
602 : :
603 [ + + ]: 444 : for (size_t i = 0; i < length; ++i) {
604 : 347 : elem = JS::UndefinedValue();
605 : :
606 [ - + ]: 347 : if (!JS_GetElement(cx, array, i, &elem)) {
607 : 0 : gjs_throw(cx, "Missing array element %" G_GSIZE_FORMAT, i);
608 : 0 : return false;
609 : : }
610 : :
611 [ + + ]: 347 : if (!js_value_to_c_strict<TAG>(cx, elem, &result[i])) {
612 : 4 : gjs_throw(cx, "Invalid element in %s array",
613 : : Gjs::static_type_name<T, TAG>());
614 : 4 : return false;
615 : : }
616 : : }
617 : :
618 : 97 : *arr_p = result.release();
619 : :
620 : 97 : return true;
621 : 101 : }
622 : :
623 : : bool
624 : 14 : gjs_array_from_strv(JSContext *context,
625 : : JS::MutableHandleValue value_p,
626 : : const char **strv)
627 : : {
628 : : guint i;
629 : 14 : JS::RootedValueVector elems(context);
630 : :
631 : : /* We treat a NULL strv as an empty array, since this function should always
632 : : * set an array value when returning true.
633 : : * Another alternative would be to set value_p to JS::NullValue, but clients
634 : : * would need to always check for both an empty array and null if that was
635 : : * the case.
636 : : */
637 [ + + + + ]: 38 : for (i = 0; strv != NULL && strv[i] != NULL; i++) {
638 [ - + ]: 24 : if (!elems.growBy(1)) {
639 : 0 : JS_ReportOutOfMemory(context);
640 : 0 : return false;
641 : : }
642 : :
643 [ - + ]: 24 : if (!gjs_string_from_utf8(context, strv[i], elems[i]))
644 : 0 : return false;
645 : : }
646 : :
647 : 14 : JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
648 [ - + ]: 14 : if (!obj)
649 : 0 : return false;
650 : :
651 : 14 : value_p.setObject(*obj);
652 : :
653 : 14 : return true;
654 : 14 : }
655 : :
656 : : bool
657 : 39 : gjs_array_to_strv(JSContext *context,
658 : : JS::Value array_value,
659 : : unsigned int length,
660 : : void **arr_p)
661 : : {
662 : 39 : return gjs_array_to_auto_array<char*>(context, array_value, length, arr_p);
663 : : }
664 : :
665 : : GJS_JSAPI_RETURN_CONVENTION
666 : 14 : static bool gjs_string_to_intarray(JSContext* context, JS::HandleString str,
667 : : GITypeTag element_type, void** arr_p,
668 : : size_t* length) {
669 : : char16_t *result16;
670 : :
671 [ + + + + ]: 14 : switch (element_type) {
672 : 9 : case GI_TYPE_TAG_INT8:
673 : : case GI_TYPE_TAG_UINT8: {
674 : 9 : JS::UniqueChars result;
675 [ - + ]: 9 : if (!gjs_string_to_utf8_n(context, str, &result, length))
676 : 0 : return false;
677 : :
678 : 9 : *arr_p = _gjs_memdup2(result.get(), *length);
679 : 9 : return true;
680 : 9 : }
681 : :
682 : 2 : case GI_TYPE_TAG_INT16:
683 : : case GI_TYPE_TAG_UINT16: {
684 [ - + ]: 2 : if (!gjs_string_get_char16_data(context, str, &result16, length))
685 : 0 : return false;
686 : 2 : *arr_p = result16;
687 : 2 : return true;
688 : : }
689 : :
690 : 2 : case GI_TYPE_TAG_UNICHAR: {
691 : : gunichar* result_ucs4;
692 [ - + ]: 2 : if (!gjs_string_to_ucs4(context, str, &result_ucs4, length))
693 : 0 : return false;
694 : 2 : *arr_p = result_ucs4;
695 : 2 : return true;
696 : : }
697 : :
698 : 1 : default:
699 : : /* can't convert a string to this type */
700 : 1 : gjs_throw(context, "Cannot convert string to array of '%s'",
701 : : g_type_tag_to_string(element_type));
702 : 1 : return false;
703 : : }
704 : : }
705 : :
706 : : GJS_JSAPI_RETURN_CONVENTION
707 : : static bool
708 : 224 : gjs_array_to_ptrarray(JSContext *context,
709 : : JS::Value array_value,
710 : : unsigned int length,
711 : : GITransfer transfer,
712 : : GITypeInfo *param_info,
713 : : void **arr_p)
714 : : {
715 : : unsigned int i;
716 : 224 : JS::RootedObject array_obj(context, array_value.toObjectOrNull());
717 : 224 : JS::RootedValue elem(context);
718 : :
719 : : /* Always one extra element, to cater for null terminated arrays */
720 : 224 : GjsAutoPointer<void*> array = array_allocate<void*>(length + 1);
721 : :
722 [ + + ]: 577 : for (i = 0; i < length; i++) {
723 : : GIArgument arg;
724 : 353 : gjs_arg_unset<void*>(&arg);
725 : :
726 : 353 : elem = JS::UndefinedValue();
727 [ - + ]: 353 : if (!JS_GetElement(context, array_obj, i, &elem)) {
728 : 0 : gjs_throw(context,
729 : : "Missing array element %u",
730 : : i);
731 : 0 : return false;
732 : : }
733 : :
734 [ - + ]: 353 : if (!gjs_value_to_g_argument(
735 : : context, elem, param_info,
736 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
737 : : &arg)) {
738 : 0 : gjs_throw(context,
739 : : "Invalid element in array");
740 : 0 : return false;
741 : : }
742 : :
743 : 353 : array[i] = gjs_arg_get<void*>(&arg);
744 : : }
745 : :
746 : 224 : *arr_p = array.release();
747 : 224 : return true;
748 : 224 : }
749 : :
750 : : GJS_JSAPI_RETURN_CONVENTION
751 : 4 : static bool gjs_array_to_flat_array(JSContext* cx, JS::HandleValue array_value,
752 : : unsigned length, GITypeInfo* param_info,
753 : : size_t param_size, void** arr_p) {
754 : 4 : g_assert((param_size > 0) &&
755 : : "Only flat arrays of elements of known size are supported");
756 : :
757 : 4 : GjsAutoPointer<uint8_t> flat_array = g_new0(uint8_t, param_size * length);
758 : :
759 : 4 : JS::RootedObject array(cx, &array_value.toObject());
760 : 4 : JS::RootedValue elem(cx);
761 [ + + ]: 9 : for (unsigned i = 0; i < length; i++) {
762 : 5 : elem = JS::UndefinedValue();
763 : :
764 [ - + ]: 5 : if (!JS_GetElement(cx, array, i, &elem)) {
765 : 0 : gjs_throw(cx, "Missing array element %u", i);
766 : 0 : return false;
767 : : }
768 : :
769 : : GIArgument arg;
770 [ - + ]: 5 : if (!gjs_value_to_g_argument(cx, elem, param_info,
771 : : GJS_ARGUMENT_ARRAY_ELEMENT,
772 : : GI_TRANSFER_NOTHING, &arg))
773 : 0 : return false;
774 : :
775 : 5 : memcpy(&flat_array[param_size * i], gjs_arg_get<void*>(&arg),
776 : : param_size);
777 : : }
778 : :
779 : 4 : *arr_p = flat_array.release();
780 : 4 : return true;
781 : 4 : }
782 : :
783 : 8 : [[nodiscard]] static bool is_gvalue(GIBaseInfo* info, GIInfoType info_type) {
784 [ - + - ]: 8 : switch (info_type) {
785 : 0 : case GI_INFO_TYPE_VALUE:
786 : 0 : return true;
787 : :
788 : 8 : case GI_INFO_TYPE_STRUCT:
789 : : case GI_INFO_TYPE_OBJECT:
790 : : case GI_INFO_TYPE_INTERFACE:
791 : : case GI_INFO_TYPE_BOXED: {
792 : 8 : GType gtype = g_registered_type_info_get_g_type(info);
793 [ + + - + ]: 8 : return g_type_is_a(gtype, G_TYPE_VALUE);
794 : : }
795 : :
796 : 0 : default:
797 : 0 : return false;
798 : : }
799 : : }
800 : :
801 : : GJS_JSAPI_RETURN_CONVENTION
802 : 321 : static bool gjs_array_to_array(JSContext* context, JS::HandleValue array_value,
803 : : size_t length, GITransfer transfer,
804 : : GITypeInfo* param_info, void** arr_p) {
805 : 321 : GITypeTag element_type = g_type_info_get_storage_type(param_info);
806 : :
807 [ + + + + : 321 : switch (element_type) {
+ - + + +
+ + + - +
+ - - ]
808 : 31 : case GI_TYPE_TAG_UTF8:
809 : 31 : return gjs_array_to_strv (context, array_value, length, arr_p);
810 : 3 : case GI_TYPE_TAG_BOOLEAN:
811 : 3 : return gjs_array_to_auto_array<gboolean, GI_TYPE_TAG_BOOLEAN>(
812 : 3 : context, array_value, length, arr_p);
813 : 2 : case GI_TYPE_TAG_UNICHAR:
814 : 2 : return gjs_array_to_auto_array<char32_t>(context, array_value, length,
815 : 2 : arr_p);
816 : 6 : case GI_TYPE_TAG_UINT8:
817 : 6 : return gjs_array_to_auto_array<uint8_t>(context, array_value, length,
818 : 6 : arr_p);
819 : 1 : case GI_TYPE_TAG_INT8:
820 : 1 : return gjs_array_to_auto_array<int8_t>(context, array_value, length,
821 : 1 : arr_p);
822 : 0 : case GI_TYPE_TAG_UINT16:
823 : 0 : return gjs_array_to_auto_array<uint16_t>(context, array_value, length,
824 : 0 : arr_p);
825 : 2 : case GI_TYPE_TAG_INT16:
826 : 2 : return gjs_array_to_auto_array<int16_t>(context, array_value, length,
827 : 2 : arr_p);
828 : 3 : case GI_TYPE_TAG_UINT32:
829 : 3 : return gjs_array_to_auto_array<uint32_t>(context, array_value, length,
830 : 3 : arr_p);
831 : 34 : case GI_TYPE_TAG_INT32:
832 : 34 : return gjs_array_to_auto_array<int32_t>(context, array_value, length,
833 : 34 : arr_p);
834 : 2 : case GI_TYPE_TAG_INT64:
835 : 2 : return gjs_array_to_auto_array<int64_t>(context, array_value, length,
836 : 2 : arr_p);
837 : 1 : case GI_TYPE_TAG_UINT64:
838 : 1 : return gjs_array_to_auto_array<uint64_t>(context, array_value, length,
839 : 1 : arr_p);
840 : 1 : case GI_TYPE_TAG_FLOAT:
841 : 1 : return gjs_array_to_auto_array<float>(context, array_value, length,
842 : 1 : arr_p);
843 : 0 : case GI_TYPE_TAG_DOUBLE:
844 : 0 : return gjs_array_to_auto_array<double>(context, array_value, length,
845 : 0 : arr_p);
846 : 3 : case GI_TYPE_TAG_GTYPE:
847 : 3 : return gjs_array_to_auto_array<GType, GI_TYPE_TAG_GTYPE>(
848 : 3 : context, array_value, length, arr_p);
849 : :
850 : : /* Everything else is a pointer type */
851 : 232 : case GI_TYPE_TAG_INTERFACE:
852 [ + + ]: 232 : if (!g_type_info_is_pointer(param_info)) {
853 : : GjsAutoBaseInfo interface_info =
854 : 8 : g_type_info_get_interface(param_info);
855 : 8 : GIInfoType info_type = g_base_info_get_type(interface_info);
856 : :
857 [ + + ]: 8 : if (is_gvalue(interface_info, info_type)) {
858 : : // Special case for GValue "flat arrays", this could also
859 : : // using the generic case, but if we do so we're leaking atm.
860 : 4 : return gjs_array_to_auto_array<GValue>(context, array_value,
861 : 4 : length, arr_p);
862 : : }
863 : :
864 : 4 : size_t element_size = gjs_type_get_element_size(
865 : : g_type_info_get_tag(param_info), param_info);
866 : :
867 [ + - ]: 4 : if (element_size) {
868 : 4 : return gjs_array_to_flat_array(context, array_value, length,
869 : 4 : param_info, element_size, arr_p);
870 : : }
871 [ - + ]: 8 : }
872 : : [[fallthrough]];
873 : : case GI_TYPE_TAG_ARRAY:
874 : : case GI_TYPE_TAG_GLIST:
875 : : case GI_TYPE_TAG_GSLIST:
876 : : case GI_TYPE_TAG_GHASH:
877 : : case GI_TYPE_TAG_ERROR:
878 : : case GI_TYPE_TAG_FILENAME:
879 [ + + ]: 224 : return gjs_array_to_ptrarray(context,
880 : : array_value,
881 : : length,
882 : : transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer,
883 : : param_info,
884 : 224 : arr_p);
885 : 0 : case GI_TYPE_TAG_VOID:
886 : : default:
887 : 0 : gjs_throw(context,
888 : : "Unhandled array element type %d", element_type);
889 : 0 : return false;
890 : : }
891 : : }
892 : :
893 : 53 : size_t gjs_type_get_element_size(GITypeTag element_type,
894 : : GITypeInfo* type_info) {
895 [ + + + - : 53 : if (g_type_info_is_pointer(type_info) &&
+ + ]
896 : : element_type != GI_TYPE_TAG_ARRAY)
897 : 30 : return sizeof(void*);
898 : :
899 [ + - - - : 23 : switch (element_type) {
- + - - -
- - - + -
- - - + -
- - - ]
900 : 1 : case GI_TYPE_TAG_BOOLEAN:
901 : 1 : return sizeof(gboolean);
902 : 0 : case GI_TYPE_TAG_INT8:
903 : 0 : return sizeof(int8_t);
904 : 0 : case GI_TYPE_TAG_UINT8:
905 : 0 : return sizeof(uint8_t);
906 : 0 : case GI_TYPE_TAG_INT16:
907 : 0 : return sizeof(int16_t);
908 : 0 : case GI_TYPE_TAG_UINT16:
909 : 0 : return sizeof(uint16_t);
910 : 2 : case GI_TYPE_TAG_INT32:
911 : 2 : return sizeof(int32_t);
912 : 0 : case GI_TYPE_TAG_UINT32:
913 : 0 : return sizeof(uint32_t);
914 : 0 : case GI_TYPE_TAG_INT64:
915 : 0 : return sizeof(int64_t);
916 : 0 : case GI_TYPE_TAG_UINT64:
917 : 0 : return sizeof(uint64_t);
918 : 0 : case GI_TYPE_TAG_FLOAT:
919 : 0 : return sizeof(float);
920 : 0 : case GI_TYPE_TAG_DOUBLE:
921 : 0 : return sizeof(double);
922 : 0 : case GI_TYPE_TAG_GTYPE:
923 : 0 : return sizeof(GType);
924 : 2 : case GI_TYPE_TAG_UNICHAR:
925 : 2 : return sizeof(char32_t);
926 : 0 : case GI_TYPE_TAG_GLIST:
927 : 0 : return sizeof(GSList);
928 : 0 : case GI_TYPE_TAG_GSLIST:
929 : 0 : return sizeof(GList);
930 : 0 : case GI_TYPE_TAG_ERROR:
931 : 0 : return sizeof(GError);
932 : 0 : case GI_TYPE_TAG_UTF8:
933 : : case GI_TYPE_TAG_FILENAME:
934 : 0 : return sizeof(char*);
935 : 18 : case GI_TYPE_TAG_INTERFACE: {
936 : 18 : GjsAutoBaseInfo interface_info = g_type_info_get_interface(type_info);
937 : :
938 [ - + - - : 18 : switch (g_base_info_get_type(interface_info)) {
- ]
939 : 0 : case GI_INFO_TYPE_ENUM:
940 : : case GI_INFO_TYPE_FLAGS:
941 : 0 : return sizeof(unsigned int);
942 : :
943 : 18 : case GI_INFO_TYPE_STRUCT:
944 : 18 : return g_struct_info_get_size(interface_info);
945 : 0 : case GI_INFO_TYPE_UNION:
946 : 0 : return g_union_info_get_size(interface_info);
947 : 0 : case GI_INFO_TYPE_VALUE:
948 : 0 : return sizeof(GValue);
949 : 0 : default:
950 : 0 : return 0;
951 : : }
952 : 18 : }
953 : :
954 : 0 : case GI_TYPE_TAG_GHASH:
955 : 0 : return sizeof(void*);
956 : :
957 : 0 : case GI_TYPE_TAG_ARRAY:
958 [ # # ]: 0 : if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
959 : 0 : int length = g_type_info_get_array_length(type_info);
960 [ # # ]: 0 : if (length < 0)
961 : 0 : return sizeof(void*);
962 : :
963 : : GjsAutoBaseInfo param_info =
964 : 0 : g_type_info_get_param_type(type_info, 0);
965 : 0 : GITypeTag param_tag = g_type_info_get_tag(param_info);
966 : : size_t param_size =
967 : 0 : gjs_type_get_element_size(param_tag, param_info);
968 : :
969 [ # # ]: 0 : if (param_size)
970 : 0 : return param_size * length;
971 [ # # ]: 0 : }
972 : :
973 : 0 : return sizeof(void*);
974 : :
975 : 0 : case GI_TYPE_TAG_VOID:
976 : 0 : break;
977 : : }
978 : :
979 : : g_return_val_if_reached(0);
980 : : }
981 : :
982 : : enum class ArrayReleaseType {
983 : : EXPLICIT_LENGTH,
984 : : ZERO_TERMINATED,
985 : : };
986 : :
987 : : template <ArrayReleaseType release_type = ArrayReleaseType::EXPLICIT_LENGTH>
988 : 356 : static inline bool gjs_g_argument_release_array_internal(
989 : : JSContext* cx, GITransfer element_transfer, GjsArgumentFlags flags,
990 : : GITypeInfo* param_type, unsigned length, GIArgument* arg) {
991 : 356 : GjsAutoPointer<uint8_t, void, g_free> arg_array =
992 : : gjs_arg_steal<uint8_t*>(arg);
993 : :
994 [ + + ]: 356 : if (!arg_array)
995 : 17 : return true;
996 : :
997 [ + + ]: 339 : if (element_transfer != GI_TRANSFER_EVERYTHING)
998 : 3 : return true;
999 : :
1000 : : if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
1001 [ + + ]: 325 : if (length == 0)
1002 : 32 : return true;
1003 : : }
1004 : :
1005 : 304 : GITypeTag type_tag = g_type_info_get_tag(param_type);
1006 : :
1007 [ + + + + ]: 597 : if (flags & GjsArgumentFlags::ARG_IN &&
1008 [ + + ]: 293 : !type_needs_release(param_type, type_tag))
1009 : 266 : return true;
1010 : :
1011 [ + + + + ]: 49 : if (flags & GjsArgumentFlags::ARG_OUT &&
1012 [ + + ]: 11 : !type_needs_out_release(param_type, type_tag))
1013 : 7 : return true;
1014 : :
1015 : 31 : size_t element_size = gjs_type_get_element_size(type_tag, param_type);
1016 [ - + ]: 31 : if G_UNLIKELY (element_size == 0)
1017 : 0 : return true;
1018 : :
1019 : 31 : bool is_pointer = g_type_info_is_pointer(param_type);
1020 [ + - + ]: 171 : for (size_t i = 0;; i++) {
1021 : : GIArgument elem;
1022 : 90 : auto* element_start = &arg_array[i * element_size];
1023 : 90 : auto* pointer =
1024 [ + + ]: 90 : is_pointer ? *reinterpret_cast<uint8_t**>(element_start) : nullptr;
1025 : :
1026 : : if constexpr (release_type == ArrayReleaseType::ZERO_TERMINATED) {
1027 [ + - ]: 34 : if (is_pointer) {
1028 [ + + ]: 34 : if (!pointer)
1029 : 9 : break;
1030 [ # # ]: 0 : } else if (*element_start == 0 &&
1031 [ # # ]: 0 : memcmp(element_start, element_start + 1,
1032 : : element_size - 1) == 0) {
1033 : 0 : break;
1034 : : }
1035 : : }
1036 : :
1037 [ + + ]: 81 : gjs_arg_set(&elem, is_pointer ? pointer : element_start);
1038 : 81 : JS::AutoSaveExceptionState saved_exc(cx);
1039 [ - + ]: 81 : if (!gjs_g_arg_release_internal(cx, element_transfer, param_type,
1040 : : type_tag, GJS_ARGUMENT_ARRAY_ELEMENT,
1041 : : flags, &elem)) {
1042 : 0 : return false;
1043 : : }
1044 : :
1045 : : if constexpr (release_type == ArrayReleaseType::EXPLICIT_LENGTH) {
1046 [ + + ]: 56 : if (i == length - 1)
1047 : 22 : break;
1048 : : }
1049 : : }
1050 : :
1051 : 31 : return true;
1052 : 356 : }
1053 : :
1054 : 6 : static GArray* garray_new_for_storage_type(unsigned length,
1055 : : GITypeTag storage_type,
1056 : : GITypeInfo* type_info) {
1057 : 6 : size_t element_size = gjs_type_get_element_size(storage_type, type_info);
1058 : 6 : return g_array_sized_new(true, false, element_size, length);
1059 : : }
1060 : :
1061 : 12 : char* gjs_argument_display_name(const char* arg_name,
1062 : : GjsArgumentType arg_type) {
1063 [ + + - - : 12 : switch (arg_type) {
- - - ]
1064 : 10 : case GJS_ARGUMENT_ARGUMENT:
1065 : 10 : return g_strdup_printf("Argument '%s'", arg_name);
1066 : 2 : case GJS_ARGUMENT_RETURN_VALUE:
1067 : 2 : return g_strdup("Return value");
1068 : 0 : case GJS_ARGUMENT_FIELD:
1069 : 0 : return g_strdup_printf("Field '%s'", arg_name);
1070 : 0 : case GJS_ARGUMENT_LIST_ELEMENT:
1071 : 0 : return g_strdup("List element");
1072 : 0 : case GJS_ARGUMENT_HASH_ELEMENT:
1073 : 0 : return g_strdup("Hash element");
1074 : 0 : case GJS_ARGUMENT_ARRAY_ELEMENT:
1075 : 0 : return g_strdup("Array element");
1076 : 0 : default:
1077 : : g_assert_not_reached ();
1078 : : }
1079 : : }
1080 : :
1081 : 12 : [[nodiscard]] static const char* type_tag_to_human_string(
1082 : : GITypeInfo* type_info) {
1083 : : GITypeTag tag;
1084 : :
1085 : 12 : tag = g_type_info_get_tag(type_info);
1086 : :
1087 [ + + ]: 12 : if (tag == GI_TYPE_TAG_INTERFACE) {
1088 : 7 : GjsAutoBaseInfo interface = g_type_info_get_interface(type_info);
1089 : 7 : return g_info_type_to_string(g_base_info_get_type(interface));
1090 : 7 : } else {
1091 : 5 : return g_type_tag_to_string(tag);
1092 : : }
1093 : : }
1094 : :
1095 : : static void
1096 : 12 : throw_invalid_argument(JSContext *context,
1097 : : JS::HandleValue value,
1098 : : GITypeInfo *arginfo,
1099 : : const char *arg_name,
1100 : : GjsArgumentType arg_type)
1101 : : {
1102 : 12 : GjsAutoChar display_name = gjs_argument_display_name(arg_name, arg_type);
1103 : :
1104 : 12 : gjs_throw(context, "Expected type %s for %s but got type '%s'",
1105 : : type_tag_to_human_string(arginfo), display_name.get(),
1106 : : JS::InformalValueTypeName(value));
1107 : 12 : }
1108 : :
1109 : : GJS_JSAPI_RETURN_CONVENTION
1110 : 368 : bool gjs_array_to_explicit_array(JSContext* context, JS::HandleValue value,
1111 : : GITypeInfo* type_info, const char* arg_name,
1112 : : GjsArgumentType arg_type, GITransfer transfer,
1113 : : GjsArgumentFlags flags, void** contents,
1114 : : size_t* length_p) {
1115 : : bool found_length;
1116 : :
1117 : : gjs_debug_marshal(
1118 : : GJS_DEBUG_GFUNCTION,
1119 : : "Converting argument '%s' JS value %s to C array, transfer %d",
1120 : : arg_name, gjs_debug_value(value).c_str(), transfer);
1121 : :
1122 : 368 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
1123 : :
1124 [ + + + - : 736 : if ((value.isNull() && !(flags & GjsArgumentFlags::MAY_BE_NULL)) ||
+ + ]
1125 [ + + + + ]: 368 : (!value.isString() && !value.isObjectOrNull())) {
1126 : 4 : throw_invalid_argument(context, value, param_info, arg_name, arg_type);
1127 : 4 : return false;
1128 : : }
1129 : :
1130 [ + + ]: 364 : if (value.isNull()) {
1131 : 12 : *contents = NULL;
1132 : 12 : *length_p = 0;
1133 [ + + ]: 352 : } else if (value.isString()) {
1134 : : /* Allow strings as int8/uint8/int16/uint16 arrays */
1135 : 14 : JS::RootedString str(context, value.toString());
1136 : 14 : GITypeTag element_tag = g_type_info_get_tag(param_info);
1137 [ + + ]: 14 : if (!gjs_string_to_intarray(context, str, element_tag, contents, length_p))
1138 : 1 : return false;
1139 [ + + ]: 14 : } else {
1140 : 338 : JS::RootedObject array_obj(context, &value.toObject());
1141 : 338 : GITypeTag element_type = g_type_info_get_tag(param_info);
1142 [ + + + - : 338 : if (JS_IsUint8Array(array_obj) && (element_type == GI_TYPE_TAG_INT8 ||
+ - + + ]
1143 : : element_type == GI_TYPE_TAG_UINT8)) {
1144 : 16 : GBytes* bytes = gjs_byte_array_get_bytes(array_obj);
1145 : 16 : *contents = g_bytes_unref_to_data(bytes, length_p);
1146 : 16 : return true;
1147 : : }
1148 : :
1149 : 322 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
1150 [ - + ]: 322 : if (!JS_HasPropertyById(context, array_obj, atoms.length(),
1151 : : &found_length))
1152 : 0 : return false;
1153 [ + + ]: 322 : if (found_length) {
1154 : : guint32 length;
1155 : :
1156 [ - + ]: 321 : if (!gjs_object_require_converted_property(
1157 : : context, array_obj, nullptr, atoms.length(), &length)) {
1158 : 4 : return false;
1159 : : } else {
1160 [ + + ]: 321 : if (!gjs_array_to_array(context,
1161 : : value,
1162 : : length,
1163 : : transfer,
1164 : : param_info,
1165 : : contents))
1166 : 4 : return false;
1167 : :
1168 : 317 : *length_p = length;
1169 : : }
1170 : : } else {
1171 : 1 : throw_invalid_argument(context, value, param_info, arg_name, arg_type);
1172 : 1 : return false;
1173 : : }
1174 [ + + ]: 338 : }
1175 : :
1176 : 342 : return true;
1177 : 368 : }
1178 : :
1179 : : namespace arg {
1180 : 17390 : [[nodiscard]] static bool is_gdk_atom(GIBaseInfo* info) {
1181 [ + + ]: 17407 : return (strcmp("Atom", g_base_info_get_name(info)) == 0 &&
1182 [ + - ]: 17407 : strcmp("Gdk", g_base_info_get_namespace(info)) == 0);
1183 : : }
1184 : : } // namespace arg
1185 : :
1186 : : static void
1187 : 5 : intern_gdk_atom(const char *name,
1188 : : GArgument *ret)
1189 : : {
1190 : : GjsAutoFunctionInfo atom_intern_fun =
1191 : 5 : g_irepository_find_by_name(nullptr, "Gdk", "atom_intern");
1192 : :
1193 : : GIArgument atom_intern_args[2];
1194 : :
1195 : : /* Can only store char * in GIArgument. First argument to gdk_atom_intern
1196 : : * is const char *, string isn't modified. */
1197 : 5 : gjs_arg_set(&atom_intern_args[0], name);
1198 : 5 : gjs_arg_set(&atom_intern_args[1], false);
1199 : :
1200 : 5 : g_function_info_invoke(atom_intern_fun,
1201 : : atom_intern_args, 2,
1202 : : nullptr, 0,
1203 : : ret,
1204 : : nullptr);
1205 : 5 : }
1206 : :
1207 : 642 : static bool value_to_interface_gi_argument(
1208 : : JSContext* cx, JS::HandleValue value, GIBaseInfo* interface_info,
1209 : : GIInfoType interface_type, GITransfer transfer, bool expect_object,
1210 : : GIArgument* arg, GjsArgumentType arg_type, GjsArgumentFlags flags,
1211 : : bool* report_type_mismatch) {
1212 : 642 : g_assert(report_type_mismatch);
1213 : : GType gtype;
1214 : :
1215 [ + - - ]: 642 : switch (interface_type) {
1216 : 642 : case GI_INFO_TYPE_BOXED:
1217 : : case GI_INFO_TYPE_ENUM:
1218 : : case GI_INFO_TYPE_FLAGS:
1219 : : case GI_INFO_TYPE_INTERFACE:
1220 : : case GI_INFO_TYPE_OBJECT:
1221 : : case GI_INFO_TYPE_STRUCT:
1222 : : case GI_INFO_TYPE_UNION:
1223 : : // These are subtypes of GIRegisteredTypeInfo for which the cast is
1224 : : // safe
1225 : 642 : gtype = g_registered_type_info_get_g_type(interface_info);
1226 : 642 : break;
1227 : :
1228 : 0 : case GI_INFO_TYPE_VALUE:
1229 : : // Special case for GValues
1230 : 0 : gtype = G_TYPE_VALUE;
1231 : 0 : break;
1232 : :
1233 : 0 : default:
1234 : 0 : gtype = G_TYPE_NONE;
1235 : : }
1236 : :
1237 : : if (gtype != G_TYPE_NONE)
1238 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION, "gtype of INTERFACE is %s",
1239 : : g_type_name(gtype));
1240 : :
1241 [ + + ]: 642 : if (gtype == G_TYPE_VALUE) {
1242 [ + - ]: 3 : if (flags & GjsArgumentFlags::CALLER_ALLOCATES) {
1243 [ - + ]: 3 : if (!gjs_value_to_g_value_no_copy(cx, value,
1244 : : gjs_arg_get<GValue*>(arg)))
1245 : 0 : return false;
1246 : :
1247 : 3 : return true;
1248 : : }
1249 : :
1250 : 0 : Gjs::AutoGValue gvalue;
1251 [ # # ]: 0 : if (!gjs_value_to_g_value(cx, value, &gvalue)) {
1252 : 0 : gjs_arg_unset<void*>(arg);
1253 : 0 : return false;
1254 : : }
1255 : :
1256 : 0 : gjs_arg_set(arg, g_boxed_copy(G_TYPE_VALUE, &gvalue));
1257 : 0 : return true;
1258 : :
1259 [ + + ]: 639 : } else if (arg::is_gdk_atom(interface_info)) {
1260 [ + + + + : 8 : if (!value.isNull() && !value.isString()) {
+ + ]
1261 : 3 : *report_type_mismatch = true;
1262 : 3 : return false;
1263 [ + + ]: 5 : } else if (value.isNull()) {
1264 : 1 : intern_gdk_atom("NONE", arg);
1265 : 1 : return true;
1266 : : }
1267 : :
1268 : 4 : JS::RootedString str(cx, value.toString());
1269 : 4 : JS::UniqueChars name(JS_EncodeStringToUTF8(cx, str));
1270 [ - + ]: 4 : if (!name)
1271 : 0 : return false;
1272 : :
1273 : 4 : intern_gdk_atom(name.get(), arg);
1274 : 4 : return true;
1275 : :
1276 [ - + ]: 635 : } else if (expect_object != value.isObjectOrNull()) {
1277 : 0 : *report_type_mismatch = true;
1278 : 0 : return false;
1279 : :
1280 [ - + ]: 631 : } else if (value.isNull()) {
1281 : 0 : gjs_arg_set(arg, nullptr);
1282 : 0 : return true;
1283 : :
1284 [ + + ]: 631 : } else if (value.isObject()) {
1285 : 525 : JS::RootedObject obj(cx, &value.toObject());
1286 [ + + - + : 927 : if (interface_type == GI_INFO_TYPE_STRUCT &&
- + ]
1287 : 402 : g_struct_info_is_gtype_struct(interface_info)) {
1288 : : GType actual_gtype;
1289 [ # # ]: 0 : if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
1290 : 0 : return false;
1291 : :
1292 [ # # ]: 0 : if (actual_gtype == G_TYPE_NONE) {
1293 : 0 : *report_type_mismatch = true;
1294 : 0 : return false;
1295 : : }
1296 : :
1297 : : // We use peek here to simplify reference counting (we just ignore
1298 : : // transfer annotation, as GType classes are never really freed)
1299 : : // We know that the GType class is referenced at least once when
1300 : : // the JS constructor is initialized.
1301 : : void* klass;
1302 [ # # # # : 0 : if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE))
# # ]
1303 : 0 : klass = g_type_default_interface_peek(actual_gtype);
1304 : : else
1305 : 0 : klass = g_type_class_peek(actual_gtype);
1306 : :
1307 : 0 : gjs_arg_set(arg, klass);
1308 : 0 : return true;
1309 : : }
1310 : :
1311 : 525 : GType arg_gtype = gtype;
1312 [ + + + + : 533 : if (interface_type == GI_INFO_TYPE_STRUCT && gtype == G_TYPE_NONE &&
+ - + + ]
1313 : 8 : !g_struct_info_is_foreign(interface_info)) {
1314 : 8 : GType actual_gtype = G_TYPE_NONE;
1315 : : // In case we have no known type from gi we should try to be
1316 : : // more dynamic and try to get the type from JS, to handle the
1317 : : // case in which we're handling a gpointer such as GTypeInstance
1318 : : // FIXME(3v1n0): would be nice to know if GI would give this info
1319 [ - + ]: 8 : if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
1320 : 0 : return false;
1321 : :
1322 [ + + ]: 8 : if (G_TYPE_IS_INSTANTIATABLE(actual_gtype))
1323 : 2 : gtype = actual_gtype;
1324 : : }
1325 : :
1326 [ - + ]: 123 : if ((interface_type == GI_INFO_TYPE_STRUCT ||
1327 [ + + + + ]: 1050 : interface_type == GI_INFO_TYPE_BOXED) &&
1328 [ + + + - ]: 402 : !g_type_is_a(gtype, G_TYPE_CLOSURE)) {
1329 : : // Handle Struct/Union first since we don't necessarily need a GType
1330 : : // for them. We special case Closures later, so skip them here.
1331 [ + - - + : 362 : if (g_type_is_a(gtype, G_TYPE_BYTES) && JS_IsUint8Array(obj)) {
- - - + ]
1332 : 0 : gjs_arg_set(arg, gjs_byte_array_get_bytes(obj));
1333 : 0 : return true;
1334 : : }
1335 [ + - - + : 362 : if (g_type_is_a(gtype, G_TYPE_ERROR)) {
- + ]
1336 : 0 : return ErrorBase::transfer_to_gi_argument(
1337 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer);
1338 : : }
1339 [ + + + - ]: 8 : if (arg_gtype != G_TYPE_NONE || gtype == G_TYPE_NONE ||
1340 [ + - ]: 2 : g_type_is_a(gtype, G_TYPE_BOXED) ||
1341 [ + + + - : 372 : g_type_is_a(gtype, G_TYPE_VALUE) ||
+ - + - +
+ ]
1342 [ - + ]: 2 : g_type_is_a(gtype, G_TYPE_VARIANT)) {
1343 : 720 : return BoxedBase::transfer_to_gi_argument(
1344 : : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype,
1345 : 360 : interface_info);
1346 : : }
1347 : : }
1348 : :
1349 [ - + ]: 165 : if (interface_type == GI_INFO_TYPE_UNION) {
1350 : 0 : return UnionBase::transfer_to_gi_argument(
1351 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype, interface_info);
1352 : : }
1353 : :
1354 [ + - ]: 165 : if (gtype != G_TYPE_NONE) {
1355 [ + + + + : 165 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
1356 : 16 : return ObjectBase::transfer_to_gi_argument(
1357 : 8 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1358 : :
1359 [ + + - + : 157 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
1360 [ - + ]: 116 : if (!gjs_typecheck_param(cx, obj, gtype, true)) {
1361 : 0 : gjs_arg_unset<void*>(arg);
1362 : 0 : return false;
1363 : : }
1364 : 116 : gjs_arg_set(arg, gjs_g_param_from_param(cx, obj));
1365 [ - + ]: 116 : if (transfer != GI_TRANSFER_NOTHING)
1366 : 0 : g_param_spec_ref(gjs_arg_get<GParamSpec*>(arg));
1367 : 116 : return true;
1368 : :
1369 [ + - + + : 41 : } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + ]
1370 [ - + - - : 40 : if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
+ - ]
1371 : : GClosure* closure =
1372 : 40 : Gjs::Closure::create_marshaled(cx, obj, "boxed");
1373 : : // GI doesn't know about floating GClosure references. We
1374 : : // guess that if this is a return value going from JS::Value
1375 : : // to GArgument, it's intended to be passed to a C API that
1376 : : // will consume the floating reference.
1377 [ - + ]: 40 : if (arg_type != GJS_ARGUMENT_RETURN_VALUE) {
1378 : 0 : g_closure_ref(closure);
1379 : 0 : g_closure_sink(closure);
1380 : : }
1381 : 40 : gjs_arg_set(arg, closure);
1382 : 40 : return true;
1383 : : }
1384 : :
1385 : : // Should have been caught above as STRUCT/BOXED/UNION
1386 : 0 : gjs_throw(
1387 : : cx,
1388 : : "Boxed type %s registered for unexpected interface_type %d",
1389 : : g_type_name(gtype), interface_type);
1390 : 0 : return false;
1391 : :
1392 [ + - ]: 1 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
1393 : 2 : return FundamentalBase::transfer_to_gi_argument(
1394 : 1 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1395 : :
1396 [ # # ]: 0 : } else if (G_TYPE_IS_INTERFACE(gtype)) {
1397 : : // Could be a GObject interface that's missing a prerequisite,
1398 : : // or could be a fundamental
1399 [ # # ]: 0 : if (ObjectBase::typecheck(cx, obj, nullptr, gtype,
1400 : : GjsTypecheckNoThrow())) {
1401 : 0 : return ObjectBase::transfer_to_gi_argument(
1402 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1403 : : }
1404 : :
1405 : : // If this typecheck fails, then it's neither an object nor a
1406 : : // fundamental
1407 : 0 : return FundamentalBase::transfer_to_gi_argument(
1408 : 0 : cx, obj, arg, GI_DIRECTION_IN, transfer, gtype);
1409 : : }
1410 : :
1411 : 0 : gjs_throw(cx, "Unhandled GType %s unpacking GIArgument from Object",
1412 : : g_type_name(gtype));
1413 : 0 : gjs_arg_unset<void*>(arg);
1414 : 0 : return false;
1415 : : }
1416 : :
1417 : 0 : gjs_debug(GJS_DEBUG_GFUNCTION,
1418 : : "conversion of JSObject value %s to type %s failed",
1419 : 0 : gjs_debug_value(value).c_str(),
1420 : : g_base_info_get_name(interface_info));
1421 : :
1422 : 0 : gjs_throw(cx,
1423 : : "Unexpected unregistered type unpacking GIArgument from "
1424 : : "Object");
1425 : 0 : return false;
1426 : :
1427 [ + + ]: 631 : } else if (value.isNumber()) {
1428 [ + + ]: 102 : if (interface_type == GI_INFO_TYPE_ENUM) {
1429 : : int64_t value_int64;
1430 : :
1431 [ + - - + ]: 196 : if (!JS::ToInt64(cx, value, &value_int64) ||
1432 [ - + ]: 98 : !_gjs_enum_value_is_valid(cx, interface_info, value_int64))
1433 : 0 : return false;
1434 : :
1435 : 98 : gjs_arg_set<int, GI_TYPE_TAG_INTERFACE>(
1436 : : arg, _gjs_enum_to_int(value_int64));
1437 : 98 : return true;
1438 : :
1439 [ + - ]: 4 : } else if (interface_type == GI_INFO_TYPE_FLAGS) {
1440 : : int64_t value_int64;
1441 : :
1442 [ + - - + ]: 8 : if (!JS::ToInt64(cx, value, &value_int64) ||
1443 [ - + ]: 4 : !_gjs_flags_value_is_valid(cx, gtype, value_int64))
1444 : 0 : return false;
1445 : :
1446 : 4 : gjs_arg_set<int, GI_TYPE_TAG_INTERFACE>(
1447 : : arg, _gjs_enum_to_int(value_int64));
1448 : 4 : return true;
1449 : :
1450 [ # # ]: 0 : } else if (gtype == G_TYPE_NONE) {
1451 : 0 : gjs_throw(cx,
1452 : : "Unexpected unregistered type unpacking GIArgument from "
1453 : : "Number");
1454 : 0 : return false;
1455 : : }
1456 : :
1457 : 0 : gjs_throw(cx, "Unhandled GType %s unpacking GIArgument from Number",
1458 : : g_type_name(gtype));
1459 : 0 : return false;
1460 : : }
1461 : :
1462 : 4 : gjs_debug(GJS_DEBUG_GFUNCTION,
1463 : : "JSObject type '%s' is neither null nor an object",
1464 : : JS::InformalValueTypeName(value));
1465 : 4 : *report_type_mismatch = true;
1466 : 4 : return false;
1467 : : }
1468 : :
1469 : : template <typename T>
1470 : 284 : GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
1471 : : JSContext* cx, const JS::HandleValue& value, GArgument* arg,
1472 : : const char* arg_name, GjsArgumentType arg_type) {
1473 : 284 : bool out_of_range = false;
1474 : :
1475 [ - + ]: 284 : if (!gjs_arg_set_from_js_value<T>(cx, value, arg, &out_of_range)) {
1476 [ # # ]: 0 : if (out_of_range) {
1477 : 0 : GjsAutoChar display_name =
1478 : : gjs_argument_display_name(arg_name, arg_type);
1479 : 0 : gjs_throw(cx, "value %s is out of range for %s (type %s)",
1480 : 0 : std::to_string(gjs_arg_get<T>(arg)).c_str(),
1481 : : display_name.get(), Gjs::static_type_name<T>());
1482 : 0 : }
1483 : :
1484 : 0 : return false;
1485 : : }
1486 : :
1487 : : gjs_debug_marshal(
1488 : : GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
1489 : : GjsAutoChar(gjs_argument_display_name(arg_name, arg_type)).get(),
1490 : : std::to_string(gjs_arg_get<T>(arg)).c_str(),
1491 : : Gjs::static_type_name<T>());
1492 : :
1493 : 284 : return true;
1494 : : }
1495 : :
1496 : 571 : static bool check_nullable_argument(JSContext* cx, const char* arg_name,
1497 : : GjsArgumentType arg_type,
1498 : : GITypeTag type_tag, GjsArgumentFlags flags,
1499 : : GIArgument* arg) {
1500 [ + + - + : 571 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL) && !gjs_arg_get<void*>(arg)) {
- + ]
1501 : : GjsAutoChar display_name =
1502 : 0 : gjs_argument_display_name(arg_name, arg_type);
1503 : 0 : gjs_throw(cx, "%s (type %s) may not be null", display_name.get(),
1504 : : g_type_tag_to_string(type_tag));
1505 : 0 : return false;
1506 : 0 : }
1507 : :
1508 : 571 : return true;
1509 : : }
1510 : :
1511 : 1114 : bool gjs_value_to_g_argument(JSContext* context, JS::HandleValue value,
1512 : : GITypeInfo* type_info, const char* arg_name,
1513 : : GjsArgumentType arg_type, GITransfer transfer,
1514 : : GjsArgumentFlags flags, GIArgument* arg) {
1515 : 1114 : GITypeTag type_tag = g_type_info_get_tag(type_info);
1516 : :
1517 : : gjs_debug_marshal(
1518 : : GJS_DEBUG_GFUNCTION,
1519 : : "Converting argument '%s' JS value %s to GIArgument type %s", arg_name,
1520 : : gjs_debug_value(value).c_str(), g_type_tag_to_string(type_tag));
1521 : :
1522 [ - + + + : 1114 : switch (type_tag) {
+ + + + +
+ + + - +
- + + + +
+ + + - ]
1523 : 0 : case GI_TYPE_TAG_VOID:
1524 : : // just so it isn't uninitialized
1525 : 0 : gjs_arg_unset<void*>(arg);
1526 : 0 : return check_nullable_argument(context, arg_name, arg_type, type_tag,
1527 : 0 : flags, arg);
1528 : :
1529 : 47 : case GI_TYPE_TAG_INT8:
1530 : 47 : return gjs_arg_set_from_js_value<int8_t>(context, value, arg, arg_name,
1531 : 47 : arg_type);
1532 : 1 : case GI_TYPE_TAG_UINT8:
1533 : 1 : return gjs_arg_set_from_js_value<uint8_t>(context, value, arg, arg_name,
1534 : 1 : arg_type);
1535 : 4 : case GI_TYPE_TAG_INT16:
1536 : 4 : return gjs_arg_set_from_js_value<int16_t>(context, value, arg, arg_name,
1537 : 4 : arg_type);
1538 : :
1539 : 2 : case GI_TYPE_TAG_UINT16:
1540 : 2 : return gjs_arg_set_from_js_value<uint16_t>(context, value, arg,
1541 : 2 : arg_name, arg_type);
1542 : :
1543 : 113 : case GI_TYPE_TAG_INT32:
1544 : 113 : return gjs_arg_set_from_js_value<int32_t>(context, value, arg, arg_name,
1545 : 113 : arg_type);
1546 : :
1547 : 7 : case GI_TYPE_TAG_UINT32:
1548 : 7 : return gjs_arg_set_from_js_value<uint32_t>(context, value, arg,
1549 : 7 : arg_name, arg_type);
1550 : :
1551 : 63 : case GI_TYPE_TAG_INT64:
1552 : 63 : return gjs_arg_set_from_js_value<int64_t>(context, value, arg, arg_name,
1553 : 63 : arg_type);
1554 : :
1555 : 4 : case GI_TYPE_TAG_UINT64:
1556 : 4 : return gjs_arg_set_from_js_value<uint64_t>(context, value, arg,
1557 : 4 : arg_name, arg_type);
1558 : :
1559 : 80 : case GI_TYPE_TAG_BOOLEAN:
1560 : 80 : gjs_arg_set(arg, JS::ToBoolean(value));
1561 : 80 : return true;
1562 : :
1563 : 17 : case GI_TYPE_TAG_FLOAT:
1564 : 17 : return gjs_arg_set_from_js_value<float>(context, value, arg, arg_name,
1565 : 17 : arg_type);
1566 : :
1567 : 26 : case GI_TYPE_TAG_DOUBLE:
1568 : 26 : return gjs_arg_set_from_js_value<double>(context, value, arg, arg_name,
1569 : 26 : arg_type);
1570 : :
1571 : 0 : case GI_TYPE_TAG_UNICHAR:
1572 [ # # ]: 0 : if (value.isString()) {
1573 [ # # ]: 0 : if (!gjs_unichar_from_string(context, value,
1574 : 0 : &gjs_arg_member<char32_t>(arg)))
1575 : 0 : return false;
1576 : : } else {
1577 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1578 : : arg_type);
1579 : 0 : return false;
1580 : : }
1581 : 0 : break;
1582 : :
1583 : 3 : case GI_TYPE_TAG_GTYPE:
1584 [ + - ]: 3 : if (value.isObjectOrNull()) {
1585 : : GType gtype;
1586 : 3 : JS::RootedObject obj(context, value.toObjectOrNull());
1587 [ - + ]: 3 : if (!gjs_gtype_get_actual_gtype(context, obj, >ype))
1588 : 0 : return false;
1589 : :
1590 [ - + ]: 3 : if (gtype == G_TYPE_INVALID)
1591 : 0 : return false;
1592 : 3 : gjs_arg_set<GType, GI_TYPE_TAG_GTYPE>(arg, gtype);
1593 [ + - ]: 3 : } else {
1594 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1595 : : arg_type);
1596 : 0 : return false;
1597 : : }
1598 : 3 : break;
1599 : :
1600 : 0 : case GI_TYPE_TAG_FILENAME:
1601 [ # # ]: 0 : if (value.isNull()) {
1602 : 0 : gjs_arg_set(arg, nullptr);
1603 [ # # ]: 0 : } else if (value.isString()) {
1604 : 0 : GjsAutoChar filename_str;
1605 [ # # ]: 0 : if (!gjs_string_to_filename(context, value, &filename_str))
1606 : 0 : return false;
1607 : :
1608 : 0 : gjs_arg_set(arg, filename_str.release());
1609 [ # # ]: 0 : } else {
1610 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1611 : : arg_type);
1612 : 0 : return false;
1613 : : }
1614 : :
1615 : 0 : return check_nullable_argument(context, arg_name, arg_type, type_tag,
1616 : 0 : flags, arg);
1617 : :
1618 : 41 : case GI_TYPE_TAG_UTF8:
1619 [ - + ]: 41 : if (value.isNull()) {
1620 : 0 : gjs_arg_set(arg, nullptr);
1621 [ + - ]: 41 : } else if (value.isString()) {
1622 : 41 : JS::RootedString str(context, value.toString());
1623 : 41 : JS::UniqueChars utf8_str(JS_EncodeStringToUTF8(context, str));
1624 [ - + ]: 41 : if (!utf8_str)
1625 : 0 : return false;
1626 : :
1627 : 82 : gjs_arg_set(arg, g_strdup(utf8_str.get()));
1628 [ + - + - ]: 41 : } else {
1629 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1630 : : arg_type);
1631 : 0 : return false;
1632 : : }
1633 : :
1634 : 41 : return check_nullable_argument(context, arg_name, arg_type, type_tag,
1635 : 41 : flags, arg);
1636 : :
1637 : 2 : case GI_TYPE_TAG_ERROR:
1638 [ - + ]: 2 : if (value.isNull()) {
1639 : 0 : gjs_arg_set(arg, nullptr);
1640 [ + - ]: 2 : } else if (value.isObject()) {
1641 : 2 : JS::RootedObject obj(context, &value.toObject());
1642 [ - + ]: 2 : if (!ErrorBase::transfer_to_gi_argument(context, obj, arg,
1643 : : GI_DIRECTION_IN, transfer))
1644 : 0 : return false;
1645 [ + - ]: 2 : } else {
1646 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1647 : : arg_type);
1648 : 0 : return false;
1649 : : }
1650 : :
1651 : 2 : return check_nullable_argument(context, arg_name, arg_type, type_tag,
1652 : 2 : flags, arg);
1653 : :
1654 : 642 : case GI_TYPE_TAG_INTERFACE:
1655 : : {
1656 : 642 : bool expect_object = true;
1657 : :
1658 : 642 : GjsAutoBaseInfo interface_info = g_type_info_get_interface(type_info);
1659 : 642 : g_assert(interface_info);
1660 : :
1661 : 642 : GIInfoType interface_type = g_base_info_get_type(interface_info);
1662 [ + + ]: 542 : if (interface_type == GI_INFO_TYPE_ENUM ||
1663 [ + + + + : 1184 : interface_type == GI_INFO_TYPE_FLAGS ||
+ + ]
1664 : 536 : arg::is_gdk_atom(interface_info))
1665 : 114 : expect_object = false;
1666 : :
1667 [ + + - + : 1055 : if (interface_type == GI_INFO_TYPE_STRUCT &&
- + ]
1668 : 413 : g_struct_info_is_foreign(interface_info)) {
1669 : 0 : return gjs_struct_foreign_convert_to_g_argument(
1670 : : context, value, interface_info, arg_name, arg_type, transfer,
1671 : 0 : flags, arg);
1672 : : }
1673 : :
1674 : 642 : bool report_type_mismatch = false;
1675 [ + + ]: 642 : if (!value_to_interface_gi_argument(
1676 : : context, value, interface_info, interface_type, transfer,
1677 : : expect_object, arg, arg_type, flags, &report_type_mismatch)) {
1678 [ + - ]: 7 : if (report_type_mismatch)
1679 : 7 : throw_invalid_argument(context, value, type_info, arg_name,
1680 : : arg_type);
1681 : :
1682 : 7 : return false;
1683 : : }
1684 : :
1685 [ + + ]: 635 : if (expect_object)
1686 : 528 : return check_nullable_argument(context, arg_name, arg_type,
1687 : 528 : type_tag, flags, arg);
1688 [ + + ]: 642 : }
1689 : 107 : break;
1690 : :
1691 : 7 : case GI_TYPE_TAG_GLIST:
1692 : 7 : return gjs_array_to_g_list(context, value, type_info, transfer,
1693 : : arg_name, arg_type,
1694 : 14 : &gjs_arg_member<GList*>(arg));
1695 : 7 : case GI_TYPE_TAG_GSLIST:
1696 : 7 : return gjs_array_to_g_list(context, value, type_info, transfer,
1697 : : arg_name, arg_type,
1698 : 14 : &gjs_arg_member<GSList*>(arg));
1699 : :
1700 : 12 : case GI_TYPE_TAG_GHASH:
1701 [ + + ]: 12 : if (value.isNull()) {
1702 : 1 : gjs_arg_set(arg, nullptr);
1703 [ - + ]: 1 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) {
1704 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1705 : : arg_type);
1706 : 0 : return false;
1707 : : }
1708 [ - + ]: 11 : } else if (!value.isObject()) {
1709 : 0 : throw_invalid_argument(context, value, type_info, arg_name,
1710 : : arg_type);
1711 : 0 : return false;
1712 : : } else {
1713 : : GHashTable *ghash;
1714 : 11 : JS::RootedObject props(context, &value.toObject());
1715 [ - + ]: 11 : if (!gjs_object_to_g_hash(context, props, type_info, transfer,
1716 : : &ghash)) {
1717 : 0 : return false;
1718 : : }
1719 : :
1720 : 11 : gjs_arg_set(arg, ghash);
1721 [ + - ]: 11 : }
1722 : 12 : break;
1723 : :
1724 : 36 : case GI_TYPE_TAG_ARRAY: {
1725 : 36 : GjsAutoPointer<void> data;
1726 : : size_t length;
1727 : 36 : GIArrayType array_type = g_type_info_get_array_type(type_info);
1728 : :
1729 : : /* First, let's handle the case where we're passed an instance
1730 : : * of Uint8Array and it needs to be marshalled to GByteArray.
1731 : : */
1732 [ + + ]: 36 : if (value.isObject()) {
1733 : 29 : JSObject* bytearray_obj = &value.toObject();
1734 [ + + + - : 29 : if (JS_IsUint8Array(bytearray_obj) &&
+ + ]
1735 : : array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
1736 : 1 : gjs_arg_set(arg, gjs_byte_array_get_byte_array(bytearray_obj));
1737 : 1 : break;
1738 : : } else {
1739 : : /* Fall through, !handled */
1740 : : }
1741 : : }
1742 : :
1743 [ + + ]: 35 : if (!gjs_array_to_explicit_array(context, value, type_info, arg_name,
1744 : : arg_type, transfer, flags, data.out(),
1745 : : &length)) {
1746 : 5 : return false;
1747 : : }
1748 : :
1749 : 30 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
1750 [ + + ]: 30 : if (array_type == GI_ARRAY_TYPE_C) {
1751 : 20 : gjs_arg_set(arg, data.release());
1752 [ + + ]: 10 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
1753 : 6 : GITypeTag storage_type = g_type_info_get_storage_type(param_info);
1754 : : GArray* array =
1755 : 6 : garray_new_for_storage_type(length, storage_type, param_info);
1756 : :
1757 [ + - ]: 6 : if (data)
1758 : 6 : g_array_append_vals(array, data, length);
1759 : 6 : gjs_arg_set(arg, array);
1760 [ + + ]: 4 : } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
1761 : 2 : GByteArray *byte_array = g_byte_array_sized_new(length);
1762 : :
1763 [ + - ]: 2 : if (data)
1764 : 2 : g_byte_array_append(byte_array, data.as<const uint8_t>(),
1765 : : length);
1766 : 2 : gjs_arg_set(arg, byte_array);
1767 [ + - ]: 2 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
1768 : 2 : GPtrArray *array = g_ptr_array_sized_new(length);
1769 : :
1770 : 2 : g_ptr_array_set_size(array, length);
1771 [ + - ]: 2 : if (data)
1772 : 2 : memcpy(array->pdata, data, sizeof(void*) * length);
1773 : 2 : gjs_arg_set(arg, array);
1774 : : }
1775 : 30 : break;
1776 [ + + ]: 66 : }
1777 : 0 : default:
1778 : 0 : g_warning("Unhandled type %s for JavaScript to GArgument conversion",
1779 : : g_type_tag_to_string(type_tag));
1780 : 0 : throw_invalid_argument(context, value, type_info, arg_name, arg_type);
1781 : 0 : return false;
1782 : : }
1783 : :
1784 : 153 : return true;
1785 : : }
1786 : :
1787 : : /* If a callback function with a return value throws, we still have
1788 : : * to return something to C. This function defines what that something
1789 : : * is. It basically boils down to memset(arg, 0, sizeof(*arg)), but
1790 : : * gives as a bit more future flexibility and also will work if
1791 : : * libffi passes us a buffer that only has room for the appropriate
1792 : : * branch of GArgument. (Currently it appears that the return buffer
1793 : : * has a fixed size large enough for the union of all types.)
1794 : : */
1795 : 283 : void gjs_gi_argument_init_default(GITypeInfo* type_info, GIArgument* arg) {
1796 : : GITypeTag type_tag;
1797 : :
1798 : 283 : type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
1799 : :
1800 [ - - - - : 283 : switch (type_tag) {
- + - - +
- + - - -
+ + - -
- ]
1801 : 0 : case GI_TYPE_TAG_VOID:
1802 : : // just so it isn't uninitialized
1803 : 0 : gjs_arg_unset<void*>(arg);
1804 : 0 : break;
1805 : 0 : case GI_TYPE_TAG_INT8:
1806 : 0 : gjs_arg_unset<int8_t>(arg);
1807 : 0 : break;
1808 : 0 : case GI_TYPE_TAG_UINT8:
1809 : 0 : gjs_arg_unset<uint8_t>(arg);
1810 : 0 : break;
1811 : 0 : case GI_TYPE_TAG_INT16:
1812 : 0 : gjs_arg_unset<int16_t>(arg);
1813 : 0 : break;
1814 : 0 : case GI_TYPE_TAG_UINT16:
1815 : 0 : gjs_arg_unset<uint16_t>(arg);
1816 : 0 : break;
1817 : 54 : case GI_TYPE_TAG_INT32:
1818 : 54 : gjs_arg_unset<int32_t>(arg);
1819 : 54 : break;
1820 : 0 : case GI_TYPE_TAG_UINT32:
1821 : 0 : gjs_arg_unset<uint32_t>(arg);
1822 : 0 : break;
1823 : 0 : case GI_TYPE_TAG_UNICHAR:
1824 : 0 : gjs_arg_unset<char32_t>(arg);
1825 : 0 : break;
1826 : 10 : case GI_TYPE_TAG_INT64:
1827 : 10 : gjs_arg_unset<int64_t>(arg);
1828 : 10 : break;
1829 : 0 : case GI_TYPE_TAG_UINT64:
1830 : 0 : gjs_arg_unset<uint64_t>(arg);
1831 : 0 : break;
1832 : 95 : case GI_TYPE_TAG_BOOLEAN:
1833 : 95 : gjs_arg_unset<bool>(arg);
1834 : 95 : break;
1835 : 0 : case GI_TYPE_TAG_FLOAT:
1836 : 0 : gjs_arg_unset<float>(arg);
1837 : 0 : break;
1838 : 0 : case GI_TYPE_TAG_DOUBLE:
1839 : 0 : gjs_arg_unset<double>(arg);
1840 : 0 : break;
1841 : 0 : case GI_TYPE_TAG_GTYPE:
1842 : 0 : gjs_arg_unset<GType, GI_TYPE_TAG_GTYPE>(arg);
1843 : 0 : break;
1844 : 2 : case GI_TYPE_TAG_FILENAME:
1845 : : case GI_TYPE_TAG_UTF8:
1846 : : case GI_TYPE_TAG_GLIST:
1847 : : case GI_TYPE_TAG_GSLIST:
1848 : : case GI_TYPE_TAG_ERROR:
1849 : 2 : gjs_arg_unset<void*>(arg);
1850 : 2 : break;
1851 : 122 : case GI_TYPE_TAG_INTERFACE: {
1852 : : GIInfoType interface_type;
1853 : :
1854 : : GjsAutoBaseInfo interface_info =
1855 : 122 : g_type_info_get_interface(type_info);
1856 : 122 : g_assert(interface_info != nullptr);
1857 : :
1858 : 122 : interface_type = g_base_info_get_type(interface_info);
1859 : :
1860 [ + + + + ]: 122 : if (interface_type == GI_INFO_TYPE_ENUM ||
1861 : : interface_type == GI_INFO_TYPE_FLAGS)
1862 : 79 : gjs_arg_unset<int, GI_TYPE_TAG_INTERFACE>(arg);
1863 : : else
1864 : 43 : gjs_arg_unset<void*>(arg);
1865 : 122 : } break;
1866 : 0 : case GI_TYPE_TAG_GHASH:
1867 : : // Possibly better to return an empty hash table?
1868 : 0 : gjs_arg_unset<GHashTable*>(arg);
1869 : 0 : break;
1870 : 0 : case GI_TYPE_TAG_ARRAY:
1871 : 0 : gjs_arg_unset<void*>(arg);
1872 : 0 : break;
1873 : 0 : default:
1874 : 0 : g_warning("Unhandled type %s for default GArgument initialization",
1875 : : g_type_tag_to_string(type_tag));
1876 : 0 : break;
1877 : : }
1878 : 283 : }
1879 : :
1880 : 34 : bool gjs_value_to_callback_out_arg(JSContext* context, JS::HandleValue value,
1881 : : GIArgInfo* arg_info, GIArgument* arg) {
1882 : 34 : GIDirection direction [[maybe_unused]] = g_arg_info_get_direction(arg_info);
1883 : 34 : g_assert(
1884 : : (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) &&
1885 : : "gjs_value_to_callback_out_arg does not handle in arguments.");
1886 : :
1887 : 34 : GjsArgumentFlags flags = GjsArgumentFlags::NONE;
1888 : : GITypeInfo type_info;
1889 : :
1890 : 34 : g_arg_info_load_type(arg_info, &type_info);
1891 : :
1892 : : // If the argument is optional and we're passed nullptr,
1893 : : // ignore the GJS value.
1894 [ - + - - : 34 : if (g_arg_info_is_optional(arg_info) && !arg)
- + ]
1895 : 0 : return true;
1896 : :
1897 : : // Otherwise, throw an error to prevent a segfault.
1898 [ - + ]: 34 : if (!arg) {
1899 : 0 : gjs_throw(context,
1900 : : "Return value %s is not optional but was passed NULL",
1901 : : g_base_info_get_name(arg_info));
1902 : 0 : return false;
1903 : : }
1904 : :
1905 [ - + ]: 34 : if (g_arg_info_may_be_null(arg_info))
1906 : 0 : flags |= GjsArgumentFlags::MAY_BE_NULL;
1907 [ + + ]: 34 : if (g_arg_info_is_caller_allocates(arg_info))
1908 : 3 : flags |= GjsArgumentFlags::CALLER_ALLOCATES;
1909 : :
1910 [ - + ]: 68 : return gjs_value_to_g_argument(
1911 : : context, value, &type_info, g_base_info_get_name(arg_info),
1912 : 34 : (g_arg_info_is_return_value(arg_info) ? GJS_ARGUMENT_RETURN_VALUE
1913 : : : GJS_ARGUMENT_ARGUMENT),
1914 : 34 : g_arg_info_get_ownership_transfer(arg_info), flags, arg);
1915 : : }
1916 : :
1917 : : template <typename T>
1918 : 61 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_array_from_g_list(
1919 : : JSContext* cx, JS::MutableHandleValue value_p, GITypeInfo* type_info,
1920 : : GITransfer transfer, T* list) {
1921 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
1922 : : GArgument arg;
1923 : 61 : JS::RootedValueVector elems(cx);
1924 : 61 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
1925 : :
1926 : 61 : g_assert(param_info);
1927 : :
1928 [ + + ]: 172 : for (size_t i = 0; list; list = list->next, ++i) {
1929 : 111 : g_type_info_argument_from_hash_pointer(param_info, list->data, &arg);
1930 : :
1931 [ - + ]: 111 : if (!elems.growBy(1)) {
1932 : 0 : JS_ReportOutOfMemory(cx);
1933 : 0 : return false;
1934 : : }
1935 : :
1936 [ - + ]: 111 : if (!gjs_value_from_g_argument(cx, elems[i], param_info,
1937 : : GJS_ARGUMENT_LIST_ELEMENT, transfer,
1938 : : &arg))
1939 : 0 : return false;
1940 : : }
1941 : :
1942 : 61 : JS::RootedObject obj(cx, JS::NewArrayObject(cx, elems));
1943 [ - + ]: 61 : if (!obj)
1944 : 0 : return false;
1945 : :
1946 : 61 : value_p.setObject(*obj);
1947 : :
1948 : 61 : return true;
1949 : 61 : }
1950 : :
1951 : : template <typename T>
1952 : 60 : GJS_JSAPI_RETURN_CONVENTION static bool gjs_g_arg_release_g_list(
1953 : : JSContext* cx, GITransfer transfer, GITypeInfo* type_info,
1954 : : GjsArgumentFlags flags, GIArgument* arg) {
1955 : : static_assert(std::is_same_v<T, GList> || std::is_same_v<T, GSList>);
1956 : 60 : GjsSmartPointer<T> list = gjs_arg_steal<T*>(arg);
1957 : :
1958 [ + + ]: 60 : if (transfer == GI_TRANSFER_CONTAINER)
1959 : 6 : return true;
1960 : :
1961 : : GIArgument elem;
1962 : 54 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
1963 : 54 : g_assert(param_info);
1964 : 54 : GITypeTag type_tag = g_type_info_get_tag(param_info);
1965 : :
1966 [ + + ]: 139 : for (T* l = list; l; l = l->next) {
1967 : 85 : gjs_arg_set(&elem, l->data);
1968 : :
1969 [ - + ]: 85 : if (!gjs_g_arg_release_internal(cx, transfer, param_info, type_tag,
1970 : : GJS_ARGUMENT_LIST_ELEMENT, flags,
1971 : : &elem)) {
1972 : 0 : return false;
1973 : : }
1974 : : }
1975 : :
1976 : 54 : return true;
1977 : 60 : }
1978 : :
1979 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
1980 : 59 : GJS_JSAPI_RETURN_CONVENTION static bool fill_vector_from_carray(
1981 : : JSContext* cx, JS::RootedValueVector& elems, // NOLINT(runtime/references)
1982 : : GITypeInfo* param_info, GIArgument* arg, void* array, size_t length,
1983 : : GITransfer transfer = GI_TRANSFER_EVERYTHING) {
1984 [ + + ]: 327 : for (size_t i = 0; i < length; i++) {
1985 : 268 : gjs_arg_set<T, TAG>(arg, *(static_cast<T*>(array) + i));
1986 : :
1987 [ - + ]: 268 : if (!gjs_value_from_g_argument(cx, elems[i], param_info,
1988 : : GJS_ARGUMENT_ARRAY_ELEMENT,
1989 : : transfer, arg))
1990 : 0 : return false;
1991 : : }
1992 : :
1993 : 59 : return true;
1994 : : }
1995 : :
1996 : : GJS_JSAPI_RETURN_CONVENTION
1997 : 82 : static bool gjs_array_from_carray_internal(
1998 : : JSContext* context, JS::MutableHandleValue value_p, GIArrayType array_type,
1999 : : GITypeInfo* param_info, GITransfer transfer, guint length, void* array) {
2000 : : GArgument arg;
2001 : : GITypeTag element_type;
2002 : : guint i;
2003 : :
2004 : 82 : element_type = g_type_info_get_tag(param_info);
2005 : :
2006 : : /* Special case array(guint8) */
2007 [ + + ]: 82 : if (element_type == GI_TYPE_TAG_UINT8) {
2008 : 4 : JSObject* obj = gjs_byte_array_from_data(context, length, array);
2009 [ - + ]: 4 : if (!obj)
2010 : 0 : return false;
2011 : 4 : value_p.setObject(*obj);
2012 : 4 : return true;
2013 : : }
2014 : :
2015 : : /* Special case array(unichar) to be a string in JS */
2016 [ + + ]: 78 : if (element_type == GI_TYPE_TAG_UNICHAR)
2017 : 1 : return gjs_string_from_ucs4(context, (gunichar *) array, length, value_p);
2018 : :
2019 : : // a null array pointer takes precedence over whatever `length` says
2020 [ + + ]: 77 : if (!array) {
2021 : 10 : JSObject* jsarray = JS::NewArrayObject(context, 0);
2022 [ - + ]: 10 : if (!jsarray)
2023 : 0 : return false;
2024 : 10 : value_p.setObject(*jsarray);
2025 : 10 : return true;
2026 : : }
2027 : :
2028 : 67 : JS::RootedValueVector elems(context);
2029 [ - + ]: 67 : if (!elems.resize(length)) {
2030 : 0 : JS_ReportOutOfMemory(context);
2031 : 0 : return false;
2032 : : }
2033 : :
2034 [ - + + - : 67 : switch (element_type) {
+ + + + -
- - + +
- ]
2035 : : /* Special cases handled above */
2036 : 0 : case GI_TYPE_TAG_UINT8:
2037 : : case GI_TYPE_TAG_UNICHAR:
2038 : : g_assert_not_reached();
2039 : 1 : case GI_TYPE_TAG_BOOLEAN:
2040 [ - + ]: 1 : if (!fill_vector_from_carray<gboolean, GI_TYPE_TAG_BOOLEAN>(
2041 : : context, elems, param_info, &arg, array, length))
2042 : 0 : return false;
2043 : 1 : break;
2044 : 2 : case GI_TYPE_TAG_INT8:
2045 [ - + ]: 2 : if (!fill_vector_from_carray<int8_t>(context, elems, param_info,
2046 : : &arg, array, length))
2047 : 0 : return false;
2048 : 2 : break;
2049 : 0 : case GI_TYPE_TAG_UINT16:
2050 [ # # ]: 0 : if (!fill_vector_from_carray<uint16_t>(context, elems, param_info,
2051 : : &arg, array, length))
2052 : 0 : return false;
2053 : 0 : break;
2054 : 1 : case GI_TYPE_TAG_INT16:
2055 [ - + ]: 1 : if (!fill_vector_from_carray<int16_t>(context, elems, param_info,
2056 : : &arg, array, length))
2057 : 0 : return false;
2058 : 1 : break;
2059 : 1 : case GI_TYPE_TAG_UINT32:
2060 [ - + ]: 1 : if (!fill_vector_from_carray<uint32_t>(context, elems, param_info,
2061 : : &arg, array, length))
2062 : 0 : return false;
2063 : 1 : break;
2064 : 23 : case GI_TYPE_TAG_INT32:
2065 [ - + ]: 23 : if (!fill_vector_from_carray<int32_t>(context, elems, param_info,
2066 : : &arg, array, length))
2067 : 0 : return false;
2068 : 23 : break;
2069 : 1 : case GI_TYPE_TAG_UINT64:
2070 [ - + ]: 1 : if (!fill_vector_from_carray<uint64_t>(context, elems, param_info,
2071 : : &arg, array, length))
2072 : 0 : return false;
2073 : 1 : break;
2074 : 0 : case GI_TYPE_TAG_INT64:
2075 [ # # ]: 0 : if (!fill_vector_from_carray<int64_t>(context, elems, param_info,
2076 : : &arg, array, length))
2077 : 0 : return false;
2078 : 0 : break;
2079 : 0 : case GI_TYPE_TAG_FLOAT:
2080 [ # # ]: 0 : if (!fill_vector_from_carray<float>(context, elems, param_info,
2081 : : &arg, array, length))
2082 : 0 : return false;
2083 : 0 : break;
2084 : 0 : case GI_TYPE_TAG_DOUBLE:
2085 [ # # ]: 0 : if (!fill_vector_from_carray<double>(context, elems, param_info,
2086 : : &arg, array, length))
2087 : 0 : return false;
2088 : 0 : break;
2089 : 13 : case GI_TYPE_TAG_INTERFACE: {
2090 : : GjsAutoBaseInfo interface_info =
2091 : 13 : g_type_info_get_interface(param_info);
2092 : 13 : GIInfoType info_type = g_base_info_get_type (interface_info);
2093 : :
2094 [ + + ]: 11 : if (array_type != GI_ARRAY_TYPE_PTR_ARRAY &&
2095 [ + - ]: 1 : (info_type == GI_INFO_TYPE_STRUCT ||
2096 [ - + ]: 1 : info_type == GI_INFO_TYPE_UNION ||
2097 [ + + + + : 24 : info_type == GI_INFO_TYPE_VALUE) &&
+ + ]
2098 : 10 : !g_type_info_is_pointer(param_info)) {
2099 : : size_t struct_size;
2100 : :
2101 [ - + ]: 8 : if (info_type == GI_INFO_TYPE_UNION)
2102 : 0 : struct_size = g_union_info_get_size(interface_info);
2103 : : else
2104 : 8 : struct_size = g_struct_info_get_size(interface_info);
2105 : :
2106 [ + + ]: 35 : for (i = 0; i < length; i++) {
2107 : 27 : gjs_arg_set(&arg,
2108 : 27 : static_cast<char*>(array) + (struct_size * i));
2109 : :
2110 [ - + ]: 27 : if (!gjs_value_from_g_argument(
2111 : : context, elems[i], param_info,
2112 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer, &arg))
2113 : 0 : return false;
2114 : : }
2115 : :
2116 : 8 : break;
2117 : : }
2118 [ + - + ]: 13 : }
2119 : : /* fallthrough */
2120 : : case GI_TYPE_TAG_GTYPE:
2121 : : case GI_TYPE_TAG_UTF8:
2122 : : case GI_TYPE_TAG_FILENAME:
2123 : : case GI_TYPE_TAG_ARRAY:
2124 : : case GI_TYPE_TAG_GLIST:
2125 : : case GI_TYPE_TAG_GSLIST:
2126 : : case GI_TYPE_TAG_GHASH:
2127 : : case GI_TYPE_TAG_ERROR:
2128 [ - + ]: 30 : if (!fill_vector_from_carray<void*>(context, elems, param_info,
2129 : : &arg, array, length, transfer))
2130 : 0 : return false;
2131 : 30 : break;
2132 : 0 : case GI_TYPE_TAG_VOID:
2133 : : default:
2134 : 0 : gjs_throw(context, "Unknown Array element-type %d", element_type);
2135 : 0 : return false;
2136 : : }
2137 : :
2138 : 134 : JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
2139 [ - + ]: 67 : if (!obj)
2140 : 0 : return false;
2141 : :
2142 : 67 : value_p.setObject(*obj);
2143 : :
2144 : 67 : return true;
2145 : 67 : }
2146 : :
2147 : : GJS_JSAPI_RETURN_CONVENTION
2148 : 15 : static bool gjs_array_from_fixed_size_array(JSContext* context,
2149 : : JS::MutableHandleValue value_p,
2150 : : GITypeInfo* type_info,
2151 : : GITransfer transfer, void* array) {
2152 : : gint length;
2153 : :
2154 : 15 : length = g_type_info_get_array_fixed_size(type_info);
2155 : :
2156 : 15 : g_assert (length != -1);
2157 : :
2158 : 15 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
2159 : :
2160 : 15 : return gjs_array_from_carray_internal(context, value_p,
2161 : : g_type_info_get_array_type(type_info),
2162 : 15 : param_info, transfer, length, array);
2163 : 15 : }
2164 : :
2165 : 45 : bool gjs_value_from_explicit_array(JSContext* context,
2166 : : JS::MutableHandleValue value_p,
2167 : : GITypeInfo* type_info, GITransfer transfer,
2168 : : GIArgument* arg, int length) {
2169 : 45 : GjsAutoTypeInfo param_info = g_type_info_get_param_type(type_info, 0);
2170 : :
2171 : 45 : return gjs_array_from_carray_internal(
2172 : : context, value_p, g_type_info_get_array_type(type_info), param_info,
2173 : 45 : transfer, length, gjs_arg_get<void*>(arg));
2174 : 45 : }
2175 : :
2176 : : GJS_JSAPI_RETURN_CONVENTION
2177 : 20 : static bool gjs_array_from_boxed_array(JSContext* context,
2178 : : JS::MutableHandleValue value_p,
2179 : : GIArrayType array_type,
2180 : : GITypeInfo* param_info,
2181 : : GITransfer transfer, GArgument* arg) {
2182 : : GArray *array;
2183 : : GPtrArray *ptr_array;
2184 : 20 : gpointer data = NULL;
2185 : 20 : gsize length = 0;
2186 : :
2187 [ - + ]: 20 : if (!gjs_arg_get<void*>(arg)) {
2188 : 0 : value_p.setNull();
2189 : 0 : return true;
2190 : : }
2191 : :
2192 [ + + - ]: 20 : switch(array_type) {
2193 : 10 : case GI_ARRAY_TYPE_BYTE_ARRAY:
2194 : : /* GByteArray is just a typedef for GArray internally */
2195 : : case GI_ARRAY_TYPE_ARRAY:
2196 : 10 : array = gjs_arg_get<GArray*>(arg);
2197 : 10 : data = array->data;
2198 : 10 : length = array->len;
2199 : 10 : break;
2200 : 10 : case GI_ARRAY_TYPE_PTR_ARRAY:
2201 : 10 : ptr_array = gjs_arg_get<GPtrArray*>(arg);
2202 : 10 : data = ptr_array->pdata;
2203 : 10 : length = ptr_array->len;
2204 : 10 : break;
2205 : 0 : case GI_ARRAY_TYPE_C: /* already checked in gjs_value_from_g_argument() */
2206 : : default:
2207 : : g_assert_not_reached();
2208 : : }
2209 : :
2210 : 20 : return gjs_array_from_carray_internal(context, value_p, array_type,
2211 : 20 : param_info, transfer, length, data);
2212 : : }
2213 : :
2214 : : GJS_JSAPI_RETURN_CONVENTION
2215 : 2 : bool gjs_array_from_g_value_array(JSContext* cx, JS::MutableHandleValue value_p,
2216 : : GITypeInfo* param_info, GITransfer transfer,
2217 : : const GValue* gvalue) {
2218 : 2 : void* data = nullptr;
2219 : 2 : size_t length = 0;
2220 : : GIArrayType array_type;
2221 : 2 : GType value_gtype = G_VALUE_TYPE(gvalue);
2222 : :
2223 : : // GByteArray is just a typedef for GArray internally
2224 [ + - + - : 4 : if (g_type_is_a(value_gtype, G_TYPE_BYTE_ARRAY) ||
- + ]
2225 [ + - - + ]: 2 : g_type_is_a(value_gtype, G_TYPE_ARRAY)) {
2226 [ # # ]: 0 : array_type = g_type_is_a(value_gtype, G_TYPE_BYTE_ARRAY)
2227 [ # # ]: 0 : ? GI_ARRAY_TYPE_BYTE_ARRAY
2228 : : : GI_ARRAY_TYPE_ARRAY;
2229 : 0 : auto* array = reinterpret_cast<GArray*>(g_value_get_boxed(gvalue));
2230 : 0 : data = array->data;
2231 : 0 : length = array->len;
2232 [ - + - - : 2 : } else if (g_type_is_a(value_gtype, G_TYPE_PTR_ARRAY)) {
+ - ]
2233 : 2 : array_type = GI_ARRAY_TYPE_PTR_ARRAY;
2234 : : auto* ptr_array =
2235 : 2 : reinterpret_cast<GPtrArray*>(g_value_get_boxed(gvalue));
2236 : 2 : data = ptr_array->pdata;
2237 : 2 : length = ptr_array->len;
2238 : : } else {
2239 : : g_assert_not_reached();
2240 : : }
2241 : :
2242 : 2 : return gjs_array_from_carray_internal(cx, value_p, array_type, param_info,
2243 : 2 : transfer, length, data);
2244 : : }
2245 : :
2246 : : template <typename T>
2247 : 441 : GJS_JSAPI_RETURN_CONVENTION static bool fill_vector_from_zero_terminated_carray(
2248 : : JSContext* cx, JS::RootedValueVector& elems, // NOLINT(runtime/references)
2249 : : GITypeInfo* param_info, GIArgument* arg, void* c_array,
2250 : : GITransfer transfer = GI_TRANSFER_EVERYTHING) {
2251 : 441 : T* array = static_cast<T*>(c_array);
2252 : :
2253 : 1053 : for (size_t i = 0;; i++) {
2254 : : if constexpr (std::is_scalar_v<T>) {
2255 [ + + ]: 1053 : if (!array[i])
2256 : 441 : break;
2257 : :
2258 : 612 : gjs_arg_set(arg, array[i]);
2259 : : } else {
2260 : 0 : uint8_t* element_start = reinterpret_cast<uint8_t*>(&array[i]);
2261 [ # # ]: 0 : if (*element_start == 0 &&
2262 : : // cppcheck-suppress pointerSize
2263 [ # # ]: 0 : memcmp(element_start, element_start + 1, sizeof(T) - 1) == 0)
2264 : 0 : break;
2265 : :
2266 : 0 : gjs_arg_set(arg, element_start);
2267 : : }
2268 : :
2269 [ - + ]: 612 : if (!elems.growBy(1)) {
2270 : 0 : JS_ReportOutOfMemory(cx);
2271 : 0 : return false;
2272 : : }
2273 : :
2274 [ - + ]: 612 : if (!gjs_value_from_g_argument(cx, elems[i], param_info,
2275 : : GJS_ARGUMENT_ARRAY_ELEMENT, transfer,
2276 : : arg))
2277 : 0 : return false;
2278 : : }
2279 : :
2280 : 441 : return true;
2281 : : }
2282 : :
2283 : : GJS_JSAPI_RETURN_CONVENTION
2284 : 442 : static bool gjs_array_from_zero_terminated_c_array(
2285 : : JSContext* context, JS::MutableHandleValue value_p, GITypeInfo* param_info,
2286 : : GITransfer transfer, void* c_array) {
2287 : : GArgument arg;
2288 : : GITypeTag element_type;
2289 : :
2290 : 442 : element_type = g_type_info_get_tag(param_info);
2291 : :
2292 : : /* Special case array(guint8) */
2293 [ - + ]: 442 : if (element_type == GI_TYPE_TAG_UINT8) {
2294 : 0 : size_t len = strlen(static_cast<char*>(c_array));
2295 : 0 : JSObject* obj = gjs_byte_array_from_data(context, len, c_array);
2296 [ # # ]: 0 : if (!obj)
2297 : 0 : return false;
2298 : 0 : value_p.setObject(*obj);
2299 : 0 : return true;
2300 : : }
2301 : :
2302 : : /* Special case array(gunichar) to JS string */
2303 [ + + ]: 442 : if (element_type == GI_TYPE_TAG_UNICHAR)
2304 : 1 : return gjs_string_from_ucs4(context, (gunichar *) c_array, -1, value_p);
2305 : :
2306 : 441 : JS::RootedValueVector elems(context);
2307 : :
2308 [ - - - - : 441 : switch (element_type) {
- - - - +
- + + -
- ]
2309 : : /* Special cases handled above. */
2310 : 0 : case GI_TYPE_TAG_UINT8:
2311 : : case GI_TYPE_TAG_UNICHAR:
2312 : : g_assert_not_reached();
2313 : 0 : case GI_TYPE_TAG_INT8:
2314 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<int8_t>(
2315 : : context, elems, param_info, &arg, c_array))
2316 : 0 : return false;
2317 : 0 : break;
2318 : 0 : case GI_TYPE_TAG_UINT16:
2319 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<uint16_t>(
2320 : : context, elems, param_info, &arg, c_array))
2321 : 0 : return false;
2322 : 0 : break;
2323 : 0 : case GI_TYPE_TAG_INT16:
2324 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<int16_t>(
2325 : : context, elems, param_info, &arg, c_array))
2326 : 0 : return false;
2327 : 0 : break;
2328 : 0 : case GI_TYPE_TAG_UINT32:
2329 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<uint32_t>(
2330 : : context, elems, param_info, &arg, c_array))
2331 : 0 : return false;
2332 : 0 : break;
2333 : 0 : case GI_TYPE_TAG_INT32:
2334 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<int32_t>(
2335 : : context, elems, param_info, &arg, c_array))
2336 : 0 : return false;
2337 : 0 : break;
2338 : 0 : case GI_TYPE_TAG_UINT64:
2339 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<uint64_t>(
2340 : : context, elems, param_info, &arg, c_array))
2341 : 0 : return false;
2342 : 0 : break;
2343 : 0 : case GI_TYPE_TAG_INT64:
2344 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<int64_t>(
2345 : : context, elems, param_info, &arg, c_array))
2346 : 0 : return false;
2347 : 0 : break;
2348 : 1 : case GI_TYPE_TAG_FLOAT:
2349 [ - + ]: 1 : if (!fill_vector_from_zero_terminated_carray<float>(
2350 : : context, elems, param_info, &arg, c_array))
2351 : 0 : return false;
2352 : 1 : break;
2353 : 0 : case GI_TYPE_TAG_DOUBLE:
2354 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<double>(
2355 : : context, elems, param_info, &arg, c_array))
2356 : 0 : return false;
2357 : 0 : break;
2358 : 416 : case GI_TYPE_TAG_INTERFACE: {
2359 : : GjsAutoBaseInfo interface_info =
2360 : 416 : g_type_info_get_interface(param_info);
2361 : :
2362 [ - + - - : 416 : if (!g_type_info_is_pointer(param_info) &&
- + ]
2363 : 0 : is_gvalue(interface_info,
2364 : : g_base_info_get_type(interface_info))) {
2365 [ # # ]: 0 : if (!fill_vector_from_zero_terminated_carray<GValue>(
2366 : : context, elems, param_info, &arg, c_array))
2367 : 0 : return false;
2368 : 0 : break;
2369 : : }
2370 : :
2371 : : [[fallthrough]];
2372 [ + - - ]: 416 : }
2373 : : case GI_TYPE_TAG_GTYPE:
2374 : : case GI_TYPE_TAG_UTF8:
2375 : : case GI_TYPE_TAG_FILENAME:
2376 : : case GI_TYPE_TAG_ARRAY:
2377 : : case GI_TYPE_TAG_GLIST:
2378 : : case GI_TYPE_TAG_GSLIST:
2379 : : case GI_TYPE_TAG_GHASH:
2380 : : case GI_TYPE_TAG_ERROR:
2381 [ - + ]: 440 : if (!fill_vector_from_zero_terminated_carray<void*>(
2382 : : context, elems, param_info, &arg, c_array, transfer))
2383 : 0 : return false;
2384 : 440 : break;
2385 : : /* Boolean zero-terminated array makes no sense, because FALSE is also
2386 : : * zero */
2387 : 0 : case GI_TYPE_TAG_BOOLEAN:
2388 : 0 : gjs_throw(context, "Boolean zero-terminated array not supported");
2389 : 0 : return false;
2390 : 0 : case GI_TYPE_TAG_VOID:
2391 : : default:
2392 : 0 : gjs_throw(context, "Unknown element-type %d", element_type);
2393 : 0 : return false;
2394 : : }
2395 : :
2396 : 882 : JS::RootedObject obj(context, JS::NewArrayObject(context, elems));
2397 [ - + ]: 441 : if (!obj)
2398 : 0 : return false;
2399 : :
2400 : 441 : value_p.setObject(*obj);
2401 : :
2402 : 441 : return true;
2403 : 441 : }
2404 : :
2405 : 20 : bool gjs_object_from_g_hash(JSContext* context, JS::MutableHandleValue value_p,
2406 : : GITypeInfo* key_param_info,
2407 : : GITypeInfo* val_param_info, GITransfer transfer,
2408 : : GHashTable* hash) {
2409 : : GHashTableIter iter;
2410 : : GArgument keyarg, valarg;
2411 : :
2412 : : // a NULL hash table becomes a null JS value
2413 [ + + ]: 20 : if (hash==NULL) {
2414 : 2 : value_p.setNull();
2415 : 2 : return true;
2416 : : }
2417 : :
2418 : 18 : JS::RootedObject obj(context, JS_NewPlainObject(context));
2419 [ - + ]: 18 : if (!obj)
2420 : 0 : return false;
2421 : :
2422 : 18 : value_p.setObject(*obj);
2423 : :
2424 : 18 : JS::RootedValue keyjs(context), valjs(context);
2425 : 18 : JS::RootedString keystr(context);
2426 : :
2427 : 18 : g_hash_table_iter_init(&iter, hash);
2428 : : void* key_pointer;
2429 : : void* val_pointer;
2430 [ + + ]: 78 : while (g_hash_table_iter_next(&iter, &key_pointer, &val_pointer)) {
2431 : 60 : g_type_info_argument_from_hash_pointer(key_param_info, key_pointer,
2432 : : &keyarg);
2433 [ - + ]: 60 : if (!gjs_value_from_g_argument(context, &keyjs, key_param_info,
2434 : : GJS_ARGUMENT_HASH_ELEMENT,
2435 : : transfer, &keyarg))
2436 : 0 : return false;
2437 : :
2438 : 60 : keystr = JS::ToString(context, keyjs);
2439 [ - + ]: 60 : if (!keystr)
2440 : 0 : return false;
2441 : :
2442 : 60 : JS::UniqueChars keyutf8(JS_EncodeStringToUTF8(context, keystr));
2443 [ - + ]: 60 : if (!keyutf8)
2444 : 0 : return false;
2445 : :
2446 : 60 : g_type_info_argument_from_hash_pointer(val_param_info, val_pointer,
2447 : : &valarg);
2448 [ - + ]: 60 : if (!gjs_value_from_g_argument(context, &valjs, val_param_info,
2449 : : GJS_ARGUMENT_HASH_ELEMENT,
2450 : : transfer, &valarg))
2451 : 0 : return false;
2452 : :
2453 [ - + ]: 60 : if (!JS_DefineProperty(context, obj, keyutf8.get(), valjs,
2454 : : JSPROP_ENUMERATE))
2455 : 0 : return false;
2456 [ + - ]: 60 : }
2457 : :
2458 : 18 : return true;
2459 : 18 : }
2460 : :
2461 : 40805 : bool gjs_value_from_g_argument(JSContext* context,
2462 : : JS::MutableHandleValue value_p,
2463 : : GITypeInfo* type_info,
2464 : : GjsArgumentType argument_type,
2465 : : GITransfer transfer, GArgument* arg) {
2466 : : GITypeTag type_tag;
2467 : :
2468 : 40805 : type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
2469 : :
2470 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
2471 : : "Converting GArgument %s to JS::Value",
2472 : : g_type_tag_to_string(type_tag));
2473 : :
2474 : 40805 : value_p.setNull();
2475 : :
2476 [ + + + + : 40805 : switch (type_tag) {
+ + + + +
+ + + + +
+ + + + +
+ + - ]
2477 : 10 : case GI_TYPE_TAG_VOID:
2478 : : // If the argument is a pointer, convert to null to match our
2479 : : // in handling.
2480 [ + - ]: 10 : if (g_type_info_is_pointer(type_info))
2481 : 10 : value_p.setNull();
2482 : : else
2483 : 0 : value_p.setUndefined();
2484 : 10 : break;
2485 : :
2486 : 1018 : case GI_TYPE_TAG_BOOLEAN:
2487 : 1018 : value_p.setBoolean(gjs_arg_get<bool>(arg));
2488 : 1018 : break;
2489 : :
2490 : 2693 : case GI_TYPE_TAG_INT32:
2491 : 2693 : value_p.setInt32(gjs_arg_get<int32_t>(arg));
2492 : 2693 : break;
2493 : :
2494 : 11158 : case GI_TYPE_TAG_UINT32:
2495 : 11158 : value_p.setNumber(gjs_arg_get<uint32_t>(arg));
2496 : 11158 : break;
2497 : :
2498 : 122 : case GI_TYPE_TAG_INT64:
2499 : 122 : value_p.setNumber(gjs_arg_get_maybe_rounded<int64_t>(arg));
2500 : 122 : break;
2501 : :
2502 : 1026 : case GI_TYPE_TAG_UINT64:
2503 : 1026 : value_p.setNumber(gjs_arg_get_maybe_rounded<uint64_t>(arg));
2504 : 1026 : break;
2505 : :
2506 : 17 : case GI_TYPE_TAG_UINT16:
2507 : 17 : value_p.setInt32(gjs_arg_get<uint16_t>(arg));
2508 : 17 : break;
2509 : :
2510 : 32 : case GI_TYPE_TAG_INT16:
2511 : 32 : value_p.setInt32(gjs_arg_get<int16_t>(arg));
2512 : 32 : break;
2513 : :
2514 : 23 : case GI_TYPE_TAG_UINT8:
2515 : 23 : value_p.setInt32(gjs_arg_get<uint8_t>(arg));
2516 : 23 : break;
2517 : :
2518 : 125 : case GI_TYPE_TAG_INT8:
2519 : 125 : value_p.setInt32(gjs_arg_get<int8_t>(arg));
2520 : 125 : break;
2521 : :
2522 : 31 : case GI_TYPE_TAG_FLOAT:
2523 : 31 : value_p.setNumber(gjs_arg_get<float>(arg));
2524 : 31 : break;
2525 : :
2526 : 40 : case GI_TYPE_TAG_DOUBLE:
2527 : 40 : value_p.setNumber(gjs_arg_get<double>(arg));
2528 : 40 : break;
2529 : :
2530 : 1391 : case GI_TYPE_TAG_GTYPE:
2531 : : {
2532 : 1391 : GType gtype = gjs_arg_get<GType, GI_TYPE_TAG_GTYPE>(arg);
2533 [ + + ]: 1391 : if (gtype == 0)
2534 : 2 : return true; /* value_p is set to JS null */
2535 : :
2536 : 1389 : JS::RootedObject obj(context, gjs_gtype_create_gtype_wrapper(context, gtype));
2537 [ - + ]: 1389 : if (!obj)
2538 : 0 : return false;
2539 : :
2540 : 1389 : value_p.setObject(*obj);
2541 : 1389 : return true;
2542 : 1389 : }
2543 : : break;
2544 : :
2545 : 3 : case GI_TYPE_TAG_UNICHAR: {
2546 : 3 : char32_t value = gjs_arg_get<char32_t>(arg);
2547 : :
2548 : : // Preserve the bidirectional mapping between 0 and ""
2549 [ + + ]: 3 : if (value == 0) {
2550 : 1 : value_p.set(JS_GetEmptyStringValue(context));
2551 : 1 : return true;
2552 [ - + ]: 2 : } else if (!g_unichar_validate(value)) {
2553 : 0 : gjs_throw(context, "Invalid unicode codepoint %" G_GUINT32_FORMAT,
2554 : : value);
2555 : 0 : return false;
2556 : : }
2557 : :
2558 : : char utf8[7];
2559 : 2 : int bytes = g_unichar_to_utf8(value, utf8);
2560 : 2 : return gjs_string_from_utf8_n(context, utf8, bytes, value_p);
2561 : : }
2562 : :
2563 : 2468 : case GI_TYPE_TAG_FILENAME:
2564 : : case GI_TYPE_TAG_UTF8: {
2565 : 2468 : const char* str = gjs_arg_get<const char*>(arg);
2566 : : // For nullptr we'll return JS::NullValue(), which is already set
2567 : : // in *value_p
2568 [ + + ]: 2468 : if (!str)
2569 : 104 : return true;
2570 : :
2571 [ + + ]: 2364 : if (type_tag == GI_TYPE_TAG_FILENAME)
2572 : 6 : return gjs_string_from_filename(context, str, -1, value_p);
2573 : :
2574 : 2358 : return gjs_string_from_utf8(context, str, value_p);
2575 : : }
2576 : :
2577 : 11 : case GI_TYPE_TAG_ERROR: {
2578 : 11 : GError* ptr = gjs_arg_get<GError*>(arg);
2579 [ + + ]: 11 : if (!ptr)
2580 : 1 : return true;
2581 : :
2582 : 10 : JSObject* obj = ErrorInstance::object_for_c_ptr(context, ptr);
2583 [ - + ]: 10 : if (!obj)
2584 : 0 : return false;
2585 : :
2586 : 10 : value_p.setObject(*obj);
2587 : 10 : return true;
2588 : : }
2589 : :
2590 : 20075 : case GI_TYPE_TAG_INTERFACE:
2591 : : {
2592 : : GIInfoType interface_type;
2593 : : GType gtype;
2594 : :
2595 : : GjsAutoBaseInfo interface_info =
2596 : 20075 : g_type_info_get_interface(type_info);
2597 : 20075 : g_assert(interface_info);
2598 : :
2599 : 20075 : interface_type = g_base_info_get_type(interface_info);
2600 : :
2601 [ - + ]: 20075 : if (interface_type == GI_INFO_TYPE_UNRESOLVED) {
2602 : 0 : gjs_throw(context,
2603 : : "Unable to resolve arg type '%s'",
2604 : : g_base_info_get_name(interface_info));
2605 : 0 : return false;
2606 : : }
2607 : :
2608 : : /* Enum/Flags are aren't pointer types, unlike the other interface subtypes */
2609 [ + + ]: 20075 : if (interface_type == GI_INFO_TYPE_ENUM) {
2610 : 2002 : int64_t value_int64 = _gjs_enum_from_int(
2611 : : interface_info,
2612 : : gjs_arg_get<int, GI_TYPE_TAG_INTERFACE>(arg));
2613 : :
2614 [ - + ]: 2002 : if (!_gjs_enum_value_is_valid(context, interface_info,
2615 : : value_int64))
2616 : 0 : return false;
2617 : :
2618 : 2002 : value_p.setNumber(static_cast<double>(value_int64));
2619 : 2002 : return true;
2620 : : }
2621 : :
2622 [ + + ]: 18073 : if (interface_type == GI_INFO_TYPE_FLAGS) {
2623 : 304 : int64_t value_int64 = _gjs_enum_from_int(
2624 : : interface_info,
2625 : : gjs_arg_get<int, GI_TYPE_TAG_INTERFACE>(arg));
2626 : :
2627 : 304 : gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
2628 : :
2629 [ + + ]: 304 : if (gtype != G_TYPE_NONE) {
2630 : : /* check make sure 32 bit flag */
2631 [ - + ]: 80 : if (static_cast<uint32_t>(value_int64) != value_int64) { /* Not a guint32 */
2632 : 0 : gjs_throw(context,
2633 : : "0x%" G_GINT64_MODIFIER "x is not a valid value for flags %s",
2634 : : value_int64, g_type_name(gtype));
2635 : 0 : return false;
2636 : : }
2637 : :
2638 : : /* Pass only valid values*/
2639 : 80 : GjsAutoTypeClass<GFlagsClass> gflags_class(gtype);
2640 : 80 : value_int64 &= gflags_class->mask;
2641 : 80 : }
2642 : :
2643 : 304 : value_p.setNumber(static_cast<double>(value_int64));
2644 : 304 : return true;
2645 : : }
2646 : :
2647 [ + + + + : 34238 : if (interface_type == GI_INFO_TYPE_STRUCT &&
+ + ]
2648 : 16469 : g_struct_info_is_foreign((GIStructInfo*)interface_info)) {
2649 : 5 : return gjs_struct_foreign_convert_from_g_argument(
2650 : 5 : context, value_p, interface_info, arg);
2651 : : }
2652 : :
2653 : : /* Everything else is a pointer type, NULL is the easy case */
2654 [ + + ]: 17764 : if (!gjs_arg_get<void*>(arg)) {
2655 : 161 : value_p.setNull();
2656 : 161 : return true;
2657 : : }
2658 : :
2659 [ + + - + : 34048 : if (interface_type == GI_INFO_TYPE_STRUCT &&
- + ]
2660 : 16445 : g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) {
2661 : : /* XXX: here we make the implicit assumption that GTypeClass is the same
2662 : : as GTypeInterface. This is true for the GType field, which is what we
2663 : : use, but not for the rest of the structure!
2664 : : */
2665 : 0 : gtype = G_TYPE_FROM_CLASS(gjs_arg_get<GTypeClass*>(arg));
2666 : :
2667 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
# # ]
2668 : 0 : return gjs_lookup_interface_constructor(context, gtype,
2669 : 0 : value_p);
2670 : : }
2671 : 0 : return gjs_lookup_object_constructor(context, gtype, value_p);
2672 : : }
2673 : :
2674 : 17603 : gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
2675 [ + + + + : 34244 : if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
+ + ]
2676 : 16641 : G_TYPE_IS_INTERFACE(gtype))
2677 : 1156 : gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
2678 : :
2679 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
2680 : : "gtype of INTERFACE is %s", g_type_name(gtype));
2681 : :
2682 : :
2683 : : /* Test GValue and GError before Struct, or it will be handled as the latter */
2684 [ + + - + : 17603 : if (g_type_is_a(gtype, G_TYPE_VALUE)) {
+ + ]
2685 : 230 : return gjs_value_from_g_value(context, value_p,
2686 : 230 : gjs_arg_get<const GValue*>(arg));
2687 : : }
2688 : :
2689 [ + - - + : 17373 : if (g_type_is_a(gtype, G_TYPE_ERROR)) {
- + ]
2690 : 0 : JSObject* obj = ErrorInstance::object_for_c_ptr(
2691 : : context, gjs_arg_get<GError*>(arg));
2692 [ # # ]: 0 : if (!obj)
2693 : 0 : return false;
2694 : 0 : value_p.setObject(*obj);
2695 : 0 : return true;
2696 : : }
2697 : :
2698 [ + + - + ]: 17373 : if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) {
2699 [ + + ]: 16215 : if (arg::is_gdk_atom(interface_info)) {
2700 : : GjsAutoFunctionInfo atom_name_fun =
2701 : 1 : g_struct_info_find_method(interface_info, "name");
2702 : : GIArgument atom_name_ret;
2703 : :
2704 : 1 : g_function_info_invoke(atom_name_fun,
2705 : : arg, 1,
2706 : : nullptr, 0,
2707 : : &atom_name_ret,
2708 : : nullptr);
2709 : :
2710 : 1 : GjsAutoChar name = gjs_arg_get<char*>(&atom_name_ret);
2711 [ - + ]: 1 : if (g_strcmp0("NONE", name) == 0) {
2712 : 0 : value_p.setNull();
2713 : 0 : return true;
2714 : : }
2715 : :
2716 : 1 : return gjs_string_from_utf8(context, name, value_p);
2717 : 1 : }
2718 : :
2719 : : JSObject *obj;
2720 : :
2721 [ + + ]: 16214 : if (gtype == G_TYPE_VARIANT) {
2722 : 3125 : transfer = GI_TRANSFER_EVERYTHING;
2723 [ + + ]: 13089 : } else if (transfer == GI_TRANSFER_CONTAINER) {
2724 [ + - ]: 5 : switch (argument_type) {
2725 : 5 : case GJS_ARGUMENT_ARRAY_ELEMENT:
2726 : : case GJS_ARGUMENT_LIST_ELEMENT:
2727 : : case GJS_ARGUMENT_HASH_ELEMENT:
2728 : 5 : transfer = GI_TRANSFER_EVERYTHING;
2729 : 5 : default:
2730 : 5 : break;
2731 : : }
2732 : : }
2733 : :
2734 [ + + ]: 16214 : if (transfer == GI_TRANSFER_EVERYTHING)
2735 : 16207 : obj = BoxedInstance::new_for_c_struct(
2736 : : context, interface_info, gjs_arg_get<void*>(arg));
2737 : : else
2738 : 7 : obj = BoxedInstance::new_for_c_struct(
2739 : : context, interface_info, gjs_arg_get<void*>(arg),
2740 : : BoxedInstance::NoCopy());
2741 : :
2742 [ - + ]: 16214 : if (!obj)
2743 : 0 : return false;
2744 : :
2745 : 16214 : value_p.setObject(*obj);
2746 : 16214 : return true;
2747 : : }
2748 : :
2749 [ + + ]: 1158 : if (interface_type == GI_INFO_TYPE_UNION) {
2750 : 2 : JSObject* obj = UnionInstance::new_for_c_union(
2751 : : context, static_cast<GIUnionInfo*>(interface_info),
2752 : : gjs_arg_get<void*>(arg));
2753 [ - + ]: 2 : if (!obj)
2754 : 0 : return false;
2755 : :
2756 : 2 : value_p.setObject(*obj);
2757 : 2 : return true;
2758 : : }
2759 : :
2760 [ + - + + : 1156 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
2761 : 1066 : g_assert(gjs_arg_get<void*>(arg) &&
2762 : : "Null arg is already handled above");
2763 : 1066 : return ObjectInstance::set_value_from_gobject(
2764 : 1066 : context, gjs_arg_get<GObject*>(arg), value_p);
2765 : : }
2766 : :
2767 [ + - + - ]: 90 : if (g_type_is_a(gtype, G_TYPE_BOXED) ||
2768 [ + - + - : 270 : g_type_is_a(gtype, G_TYPE_ENUM) ||
+ - - + ]
2769 [ - + ]: 90 : g_type_is_a(gtype, G_TYPE_FLAGS)) {
2770 : : /* Should have been handled above */
2771 : 0 : gjs_throw(context,
2772 : : "Type %s registered for unexpected interface_type %d",
2773 : : g_type_name(gtype),
2774 : : interface_type);
2775 : 0 : return false;
2776 : : }
2777 : :
2778 [ + - + + : 90 : if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
2779 : 89 : JSObject* obj = gjs_param_from_g_param(
2780 : 89 : context, G_PARAM_SPEC(gjs_arg_get<GParamSpec*>(arg)));
2781 [ - + ]: 89 : if (!obj)
2782 : 0 : return false;
2783 : 89 : value_p.setObject(*obj);
2784 : 89 : return true;
2785 : : }
2786 : :
2787 [ - + ]: 1 : if (gtype == G_TYPE_NONE) {
2788 : 0 : gjs_throw(context, "Unexpected unregistered type packing GArgument into JS::Value");
2789 : 0 : return false;
2790 : : }
2791 : :
2792 [ - + - - : 1 : if (G_TYPE_IS_INSTANTIATABLE(gtype) || G_TYPE_IS_INTERFACE(gtype)) {
+ - ]
2793 : 1 : JSObject* obj = FundamentalInstance::object_for_c_ptr(
2794 : : context, gjs_arg_get<void*>(arg));
2795 [ - + ]: 1 : if (!obj)
2796 : 0 : return false;
2797 : 1 : value_p.setObject(*obj);
2798 : 1 : return true;
2799 : : }
2800 : :
2801 : 0 : gjs_throw(context,
2802 : : "Unhandled GType %s packing GArgument into JS::Value",
2803 : : g_type_name(gtype));
2804 : 0 : return false;
2805 : 20075 : }
2806 : :
2807 : 481 : case GI_TYPE_TAG_ARRAY:
2808 [ + + ]: 481 : if (!gjs_arg_get<void*>(arg)) {
2809 : : /* OK, but no conversion to do */
2810 [ + + ]: 479 : } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
2811 [ + + ]: 457 : if (g_type_info_is_zero_terminated(type_info)) {
2812 : : GjsAutoBaseInfo param_info =
2813 : 442 : g_type_info_get_param_type(type_info, 0);
2814 : 442 : g_assert(param_info != nullptr);
2815 : :
2816 : 442 : return gjs_array_from_zero_terminated_c_array(
2817 : : context, value_p, param_info, transfer,
2818 : 442 : gjs_arg_get<void*>(arg));
2819 : 442 : } else {
2820 : : /* arrays with length are handled outside of this function */
2821 : 15 : g_assert(((void) "Use gjs_value_from_explicit_array() for "
2822 : : "arrays with length param",
2823 : : g_type_info_get_array_length(type_info) == -1));
2824 : 15 : return gjs_array_from_fixed_size_array(context, value_p,
2825 : : type_info, transfer,
2826 : 15 : gjs_arg_get<void*>(arg));
2827 : : }
2828 [ + + ]: 22 : } else if (g_type_info_get_array_type(type_info) ==
2829 : : GI_ARRAY_TYPE_BYTE_ARRAY) {
2830 : 2 : auto* byte_array = gjs_arg_get<GByteArray*>(arg);
2831 : : JSObject* array =
2832 : 2 : gjs_byte_array_from_byte_array(context, byte_array);
2833 [ - + ]: 2 : if (!array) {
2834 : 0 : gjs_throw(context,
2835 : : "Couldn't convert GByteArray to a Uint8Array");
2836 : 0 : return false;
2837 : : }
2838 : 2 : value_p.setObject(*array);
2839 : : } else {
2840 : : // this assumes the array type is GArray or GPtrArray
2841 : : GjsAutoTypeInfo param_info =
2842 : 20 : g_type_info_get_param_type(type_info, 0);
2843 : 20 : g_assert(param_info != nullptr);
2844 : :
2845 : 20 : return gjs_array_from_boxed_array(
2846 : : context, value_p, g_type_info_get_array_type(type_info),
2847 : 20 : param_info, transfer, arg);
2848 : 20 : }
2849 : 4 : break;
2850 : :
2851 : 45 : case GI_TYPE_TAG_GLIST:
2852 : 45 : return gjs_array_from_g_list(context, value_p, type_info, transfer,
2853 : 45 : gjs_arg_get<GList*>(arg));
2854 : 16 : case GI_TYPE_TAG_GSLIST:
2855 : 16 : return gjs_array_from_g_list(context, value_p, type_info, transfer,
2856 : 16 : gjs_arg_get<GSList*>(arg));
2857 : :
2858 : 20 : case GI_TYPE_TAG_GHASH:
2859 : : {
2860 : : GjsAutoTypeInfo key_param_info =
2861 : 20 : g_type_info_get_param_type(type_info, 0);
2862 : : GjsAutoTypeInfo val_param_info =
2863 : 20 : g_type_info_get_param_type(type_info, 1);
2864 : 20 : g_assert(key_param_info != nullptr);
2865 : 20 : g_assert(val_param_info != nullptr);
2866 : :
2867 : 20 : return gjs_object_from_g_hash(context, value_p, key_param_info,
2868 : : val_param_info, transfer,
2869 : 20 : gjs_arg_get<GHashTable*>(arg));
2870 : 20 : }
2871 : : break;
2872 : :
2873 : 0 : default:
2874 : 0 : g_warning("Unhandled type %s converting GArgument to JavaScript",
2875 : : g_type_tag_to_string(type_tag));
2876 : 0 : return false;
2877 : : }
2878 : :
2879 : 16299 : return true;
2880 : : }
2881 : :
2882 : : struct GHR_closure {
2883 : : JSContext *context;
2884 : : GjsAutoTypeInfo key_param_info, val_param_info;
2885 : : GITransfer transfer;
2886 : : GjsArgumentFlags flags;
2887 : : bool failed;
2888 : : };
2889 : :
2890 : : static gboolean
2891 : 60 : gjs_ghr_helper(gpointer key, gpointer val, gpointer user_data) {
2892 : 60 : GHR_closure *c = (GHR_closure *) user_data;
2893 : : GArgument key_arg, val_arg;
2894 : 60 : gjs_arg_set(&key_arg, key);
2895 : 60 : gjs_arg_set(&val_arg, val);
2896 [ - + ]: 60 : if (!gjs_g_arg_release_internal(c->context, c->transfer, c->key_param_info,
2897 : : g_type_info_get_tag(c->key_param_info),
2898 : : GJS_ARGUMENT_HASH_ELEMENT, c->flags,
2899 : : &key_arg))
2900 : 0 : c->failed = true;
2901 : :
2902 : 60 : GITypeTag val_type = g_type_info_get_tag(c->val_param_info);
2903 : :
2904 [ + + ]: 60 : switch (val_type) {
2905 : 16 : case GI_TYPE_TAG_DOUBLE:
2906 : : case GI_TYPE_TAG_FLOAT:
2907 : : case GI_TYPE_TAG_INT64:
2908 : : case GI_TYPE_TAG_UINT64:
2909 [ + - ]: 16 : g_clear_pointer(&gjs_arg_member<void*>(&val_arg), g_free);
2910 : 16 : break;
2911 : :
2912 : 44 : default:
2913 [ - + ]: 44 : if (!gjs_g_arg_release_internal(
2914 : : c->context, c->transfer, c->val_param_info, val_type,
2915 : : GJS_ARGUMENT_HASH_ELEMENT, c->flags, &val_arg))
2916 : 0 : c->failed = true;
2917 : : }
2918 : :
2919 : 60 : return true;
2920 : : }
2921 : :
2922 : : /* We need to handle GI_TRANSFER_NOTHING differently for out parameters
2923 : : * (free nothing) and for in parameters (free any temporaries we've
2924 : : * allocated
2925 : : */
2926 : 33977 : constexpr static bool is_transfer_in_nothing(GITransfer transfer,
2927 : : GjsArgumentFlags flags) {
2928 [ + + + + ]: 33977 : return (transfer == GI_TRANSFER_NOTHING) && (flags & GjsArgumentFlags::ARG_IN);
2929 : : }
2930 : :
2931 : : GJS_JSAPI_RETURN_CONVENTION
2932 : 15792 : static bool gjs_g_arg_release_internal(
2933 : : JSContext* context, GITransfer transfer, GITypeInfo* type_info,
2934 : : GITypeTag type_tag, [[maybe_unused]] GjsArgumentType argument_type,
2935 : : GjsArgumentFlags flags, GIArgument* arg) {
2936 : 15792 : g_assert(transfer != GI_TRANSFER_NOTHING ||
2937 : : flags != GjsArgumentFlags::NONE);
2938 : :
2939 [ + + + + : 15792 : switch (type_tag) {
+ + + +
- ]
2940 : 564 : case GI_TYPE_TAG_VOID:
2941 : : case GI_TYPE_TAG_BOOLEAN:
2942 : : case GI_TYPE_TAG_INT8:
2943 : : case GI_TYPE_TAG_UINT8:
2944 : : case GI_TYPE_TAG_INT16:
2945 : : case GI_TYPE_TAG_UINT16:
2946 : : case GI_TYPE_TAG_INT32:
2947 : : case GI_TYPE_TAG_UINT32:
2948 : : case GI_TYPE_TAG_INT64:
2949 : : case GI_TYPE_TAG_UINT64:
2950 : : case GI_TYPE_TAG_FLOAT:
2951 : : case GI_TYPE_TAG_DOUBLE:
2952 : : case GI_TYPE_TAG_UNICHAR:
2953 : : case GI_TYPE_TAG_GTYPE:
2954 : 564 : break;
2955 : :
2956 : 251 : case GI_TYPE_TAG_FILENAME:
2957 : : case GI_TYPE_TAG_UTF8:
2958 [ + + ]: 251 : g_clear_pointer(&gjs_arg_member<char*>(arg), g_free);
2959 : 251 : break;
2960 : :
2961 : 10 : case GI_TYPE_TAG_ERROR:
2962 [ + + ]: 10 : if (!is_transfer_in_nothing(transfer, flags))
2963 : 8 : g_clear_error(&gjs_arg_member<GError*>(arg));
2964 : 10 : break;
2965 : :
2966 : 14831 : case GI_TYPE_TAG_INTERFACE:
2967 : : {
2968 : : GIInfoType interface_type;
2969 : : GType gtype;
2970 : :
2971 : : GjsAutoBaseInfo interface_info =
2972 : 14831 : g_type_info_get_interface(type_info);
2973 : 14831 : g_assert(interface_info);
2974 : :
2975 : 14831 : interface_type = g_base_info_get_type(interface_info);
2976 : :
2977 [ + + + + : 29161 : if (interface_type == GI_INFO_TYPE_STRUCT &&
+ + ]
2978 : 14330 : g_struct_info_is_foreign((GIStructInfo*)interface_info))
2979 : 4 : return gjs_struct_foreign_release_g_argument(context,
2980 : 4 : transfer, interface_info, arg);
2981 : :
2982 [ + + + + ]: 14827 : if (interface_type == GI_INFO_TYPE_ENUM || interface_type == GI_INFO_TYPE_FLAGS)
2983 : 13 : return true;
2984 : :
2985 : : /* Anything else is a pointer */
2986 [ + + ]: 14814 : if (!gjs_arg_get<void*>(arg))
2987 : 73 : return true;
2988 : :
2989 : 14741 : gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
2990 [ + + + + : 29124 : if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
+ + ]
2991 : 14383 : G_TYPE_IS_INTERFACE(gtype))
2992 : 433 : gtype = G_TYPE_FROM_INSTANCE(gjs_arg_get<GTypeInstance*>(arg));
2993 : :
2994 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
2995 : : "gtype of INTERFACE is %s", g_type_name(gtype));
2996 : :
2997 : : /* In gjs_value_from_g_argument we handle Struct/Union types without a
2998 : : * registered GType, but here we are specifically handling a GArgument that
2999 : : * *owns* its value, and that is non-sensical for such types, so we
3000 : : * don't have to worry about it.
3001 : : */
3002 : :
3003 [ + - + + : 14741 : if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
+ + ]
3004 [ + - ]: 336 : if (!is_transfer_in_nothing(transfer, flags))
3005 [ + - ]: 336 : g_clear_object(&gjs_arg_member<GObject*>(arg));
3006 [ + - + + : 14405 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
3007 [ + - ]: 81 : if (!is_transfer_in_nothing(transfer, flags))
3008 [ + - ]: 81 : g_clear_pointer(&gjs_arg_member<GParamSpec*>(arg),
3009 : : g_param_spec_unref);
3010 [ + - - + : 14324 : } else if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
- + ]
3011 [ # # ]: 0 : g_clear_pointer(&gjs_arg_member<GClosure*>(arg),
3012 : : g_closure_unref);
3013 [ + + - + : 14324 : } else if (g_type_is_a(gtype, G_TYPE_VALUE)) {
+ + ]
3014 : : /* G_TYPE_VALUE is-a G_TYPE_BOXED, but we special case it */
3015 [ + + ]: 29 : if (g_type_info_is_pointer (type_info))
3016 : 20 : g_boxed_free(gtype, gjs_arg_steal<void*>(arg));
3017 : : else
3018 [ + - ]: 9 : g_clear_pointer(&gjs_arg_member<GValue*>(arg),
3019 : : g_value_unset);
3020 [ + - + + : 14295 : } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + ]
3021 [ + - ]: 12452 : if (!is_transfer_in_nothing(transfer, flags))
3022 : 12452 : g_boxed_free(gtype, gjs_arg_steal<void*>(arg));
3023 [ + + - + : 1843 : } else if (g_type_is_a(gtype, G_TYPE_VARIANT)) {
+ + ]
3024 [ + - ]: 1827 : if (!is_transfer_in_nothing(transfer, flags))
3025 [ + - ]: 1827 : g_clear_pointer(&gjs_arg_member<GVariant*>(arg),
3026 : : g_variant_unref);
3027 [ - + ]: 16 : } else if (gtype == G_TYPE_NONE) {
3028 [ # # ]: 0 : if (!is_transfer_in_nothing(transfer, flags)) {
3029 : 0 : gjs_throw(context, "Don't know how to release GArgument: not an object or boxed type");
3030 : 0 : return false;
3031 : : }
3032 [ + - ]: 16 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
3033 [ + - ]: 16 : if (!is_transfer_in_nothing(transfer, flags)) {
3034 : : auto* priv =
3035 : 16 : FundamentalPrototype::for_gtype(context, gtype);
3036 : 16 : priv->call_unref_function(gjs_arg_steal<void*>(arg));
3037 : : }
3038 : : } else {
3039 : 0 : gjs_throw(context, "Unhandled GType %s releasing GArgument",
3040 : : g_type_name(gtype));
3041 : 0 : return false;
3042 : : }
3043 : 14741 : return true;
3044 : 14831 : }
3045 : :
3046 : 53 : case GI_TYPE_TAG_ARRAY:
3047 : : {
3048 : 53 : GIArrayType array_type = g_type_info_get_array_type(type_info);
3049 : :
3050 [ + + ]: 53 : if (!gjs_arg_get<void*>(arg)) {
3051 : : /* OK */
3052 [ + + ]: 52 : } else if (array_type == GI_ARRAY_TYPE_C) {
3053 : : GjsAutoTypeInfo param_info =
3054 : 27 : g_type_info_get_param_type(type_info, 0);
3055 : : GITypeTag element_type;
3056 : :
3057 : 27 : element_type = g_type_info_get_tag(param_info);
3058 : :
3059 [ + + + - : 27 : switch (element_type) {
- ]
3060 : 17 : case GI_TYPE_TAG_UTF8:
3061 : : case GI_TYPE_TAG_FILENAME:
3062 [ + + ]: 17 : if (transfer == GI_TRANSFER_CONTAINER)
3063 [ + - ]: 2 : g_clear_pointer(&gjs_arg_member<void*>(arg),
3064 : : g_free);
3065 : : else
3066 [ + - ]: 15 : g_clear_pointer(&gjs_arg_member<GStrv>(arg),
3067 : : g_strfreev);
3068 : 17 : break;
3069 : :
3070 : 5 : case GI_TYPE_TAG_BOOLEAN:
3071 : : case GI_TYPE_TAG_UINT8:
3072 : : case GI_TYPE_TAG_UINT16:
3073 : : case GI_TYPE_TAG_UINT32:
3074 : : case GI_TYPE_TAG_UINT64:
3075 : : case GI_TYPE_TAG_INT8:
3076 : : case GI_TYPE_TAG_INT16:
3077 : : case GI_TYPE_TAG_INT32:
3078 : : case GI_TYPE_TAG_INT64:
3079 : : case GI_TYPE_TAG_FLOAT:
3080 : : case GI_TYPE_TAG_DOUBLE:
3081 : : case GI_TYPE_TAG_UNICHAR:
3082 : : case GI_TYPE_TAG_GTYPE:
3083 [ + - ]: 5 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
3084 : 5 : break;
3085 : :
3086 : 5 : case GI_TYPE_TAG_INTERFACE:
3087 [ + + ]: 5 : if (!g_type_info_is_pointer(param_info)) {
3088 : : GjsAutoBaseInfo interface_info =
3089 : 2 : g_type_info_get_interface(param_info);
3090 : 2 : GIInfoType info_type = g_base_info_get_type(interface_info);
3091 [ - + - - ]: 2 : if (info_type == GI_INFO_TYPE_STRUCT ||
3092 : : info_type == GI_INFO_TYPE_UNION) {
3093 [ + - ]: 2 : g_clear_pointer(&gjs_arg_member<void*>(arg), g_free);
3094 : 2 : break;
3095 : : }
3096 [ - + ]: 2 : }
3097 : : [[fallthrough]];
3098 : : case GI_TYPE_TAG_GLIST:
3099 : : case GI_TYPE_TAG_GSLIST:
3100 : : case GI_TYPE_TAG_ARRAY:
3101 : : case GI_TYPE_TAG_GHASH:
3102 : : case GI_TYPE_TAG_ERROR: {
3103 : 3 : GITransfer element_transfer = transfer;
3104 : :
3105 [ - + - - ]: 3 : if (argument_type != GJS_ARGUMENT_ARGUMENT &&
3106 : : transfer != GI_TRANSFER_EVERYTHING)
3107 : 0 : element_transfer = GI_TRANSFER_NOTHING;
3108 : :
3109 [ + + ]: 3 : if (g_type_info_is_zero_terminated(type_info)) {
3110 : : return gjs_g_argument_release_array_internal<
3111 : 4 : ArrayReleaseType::ZERO_TERMINATED>(
3112 : : context, element_transfer,
3113 : 4 : flags | GjsArgumentFlags::ARG_OUT, param_info, 0, arg);
3114 : : } else {
3115 : : return gjs_g_argument_release_array_internal<
3116 : 2 : ArrayReleaseType::EXPLICIT_LENGTH>(
3117 : : context, element_transfer,
3118 : 2 : flags | GjsArgumentFlags::ARG_OUT, param_info,
3119 : 2 : g_type_info_get_array_fixed_size(type_info), arg);
3120 : : }
3121 : : }
3122 : :
3123 : 0 : case GI_TYPE_TAG_VOID:
3124 : : default:
3125 : 0 : gjs_throw(context,
3126 : : "Releasing a C array with explicit length, that was nested"
3127 : : "inside another container. This is not supported (and will leak)");
3128 : 0 : return false;
3129 : : }
3130 [ + + + + ]: 52 : } else if (array_type == GI_ARRAY_TYPE_ARRAY) {
3131 : : GITypeTag element_type;
3132 : :
3133 : : GjsAutoTypeInfo param_info =
3134 : 11 : g_type_info_get_param_type(type_info, 0);
3135 : 11 : element_type = g_type_info_get_tag(param_info);
3136 : :
3137 [ + + - ]: 11 : switch (element_type) {
3138 : 4 : case GI_TYPE_TAG_BOOLEAN:
3139 : : case GI_TYPE_TAG_UNICHAR:
3140 : : case GI_TYPE_TAG_UINT8:
3141 : : case GI_TYPE_TAG_UINT16:
3142 : : case GI_TYPE_TAG_UINT32:
3143 : : case GI_TYPE_TAG_UINT64:
3144 : : case GI_TYPE_TAG_INT8:
3145 : : case GI_TYPE_TAG_INT16:
3146 : : case GI_TYPE_TAG_INT32:
3147 : : case GI_TYPE_TAG_INT64:
3148 : : case GI_TYPE_TAG_FLOAT:
3149 : : case GI_TYPE_TAG_DOUBLE:
3150 : : case GI_TYPE_TAG_GTYPE:
3151 [ + - ]: 4 : g_clear_pointer(&gjs_arg_member<GArray*>(arg), g_array_unref);
3152 : 4 : break;
3153 : :
3154 : 7 : case GI_TYPE_TAG_UTF8:
3155 : : case GI_TYPE_TAG_FILENAME:
3156 : : case GI_TYPE_TAG_ARRAY:
3157 : : case GI_TYPE_TAG_INTERFACE:
3158 : : case GI_TYPE_TAG_GLIST:
3159 : : case GI_TYPE_TAG_GSLIST:
3160 : : case GI_TYPE_TAG_GHASH:
3161 : : case GI_TYPE_TAG_ERROR: {
3162 : : GjsAutoPointer<GArray, GArray, g_array_unref> array =
3163 : 7 : gjs_arg_steal<GArray*>(arg);
3164 : :
3165 [ + + + + : 12 : if (transfer != GI_TRANSFER_CONTAINER &&
+ + ]
3166 : 5 : type_needs_out_release(param_info, element_type)) {
3167 : : guint i;
3168 : :
3169 [ + + ]: 16 : for (i = 0; i < array->len; i++) {
3170 : : GArgument arg_iter;
3171 : :
3172 : 12 : gjs_arg_set(&arg_iter,
3173 : 12 : g_array_index(array, gpointer, i));
3174 [ - + ]: 12 : if (!gjs_g_arg_release_internal(
3175 : : context, transfer, param_info, element_type,
3176 : : GJS_ARGUMENT_ARRAY_ELEMENT, flags, &arg_iter))
3177 : 0 : return false;
3178 : : }
3179 : : }
3180 : :
3181 : 7 : break;
3182 [ - + ]: 7 : }
3183 : :
3184 : 0 : case GI_TYPE_TAG_VOID:
3185 : : default:
3186 : 0 : gjs_throw(context,
3187 : : "Don't know how to release GArray element-type %d",
3188 : : element_type);
3189 : 0 : return false;
3190 : : }
3191 : :
3192 [ + - + + ]: 25 : } else if (array_type == GI_ARRAY_TYPE_BYTE_ARRAY) {
3193 [ + - ]: 5 : g_clear_pointer(&gjs_arg_member<GByteArray*>(arg),
3194 : : g_byte_array_unref);
3195 [ + - ]: 9 : } else if (array_type == GI_ARRAY_TYPE_PTR_ARRAY) {
3196 : : GjsAutoTypeInfo param_info =
3197 : 9 : g_type_info_get_param_type(type_info, 0);
3198 : : GjsAutoPointer<GPtrArray, GPtrArray, g_ptr_array_unref> array =
3199 : 9 : gjs_arg_steal<GPtrArray*>(arg);
3200 : :
3201 [ + + ]: 9 : if (transfer != GI_TRANSFER_CONTAINER) {
3202 : : guint i;
3203 : :
3204 [ + + ]: 22 : for (i = 0; i < array->len; i++) {
3205 : : GArgument arg_iter;
3206 : :
3207 : 16 : gjs_arg_set(&arg_iter, g_ptr_array_index(array, i));
3208 [ - + ]: 16 : if (!gjs_g_argument_release(context, transfer, param_info,
3209 : : flags, &arg_iter))
3210 : 0 : return false;
3211 : : }
3212 : : }
3213 [ + - + - ]: 9 : } else {
3214 : : g_assert_not_reached();
3215 : : }
3216 : 50 : break;
3217 : : }
3218 : :
3219 : 44 : case GI_TYPE_TAG_GLIST:
3220 : 44 : return gjs_g_arg_release_g_list<GList>(context, transfer, type_info,
3221 : 44 : flags, arg);
3222 : :
3223 : 16 : case GI_TYPE_TAG_GSLIST:
3224 : 16 : return gjs_g_arg_release_g_list<GSList>(context, transfer, type_info,
3225 : 16 : flags, arg);
3226 : :
3227 : 23 : case GI_TYPE_TAG_GHASH:
3228 [ + + ]: 23 : if (gjs_arg_get<GHashTable*>(arg)) {
3229 : : GjsAutoPointer<GHashTable, GHashTable, g_hash_table_destroy>
3230 : 21 : hash_table = gjs_arg_steal<GHashTable*>(arg);
3231 [ + + ]: 21 : if (transfer == GI_TRANSFER_CONTAINER)
3232 : 3 : g_hash_table_remove_all(hash_table);
3233 : : else {
3234 : 18 : GHR_closure c = {context, nullptr, nullptr,
3235 : 18 : transfer, flags, false};
3236 : :
3237 : 18 : c.key_param_info = g_type_info_get_param_type(type_info, 0);
3238 : 18 : g_assert(c.key_param_info != nullptr);
3239 : 18 : c.val_param_info = g_type_info_get_param_type(type_info, 1);
3240 : 18 : g_assert(c.val_param_info != nullptr);
3241 : :
3242 : 18 : g_hash_table_foreach_steal(hash_table, gjs_ghr_helper, &c);
3243 : :
3244 [ - + ]: 18 : if (c.failed)
3245 : 0 : return false;
3246 [ + - ]: 18 : }
3247 [ + - ]: 21 : }
3248 : 23 : break;
3249 : :
3250 : 0 : default:
3251 : 0 : g_warning("Unhandled type %s releasing GArgument",
3252 : : g_type_tag_to_string(type_tag));
3253 : 0 : return false;
3254 : : }
3255 : :
3256 : 898 : return true;
3257 : : }
3258 : :
3259 : 34719 : bool gjs_g_argument_release(JSContext* cx, GITransfer transfer,
3260 : : GITypeInfo* type_info, GjsArgumentFlags flags,
3261 : : GIArgument* arg) {
3262 : : GITypeTag type_tag;
3263 : :
3264 [ + + + + ]: 53974 : if (transfer == GI_TRANSFER_NOTHING &&
3265 [ + + ]: 19255 : !is_transfer_in_nothing(transfer, flags))
3266 : 19249 : return true;
3267 : :
3268 : 15470 : type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
3269 : :
3270 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3271 : : "Releasing GArgument %s out param or return value",
3272 : : g_type_tag_to_string(type_tag));
3273 : :
3274 : 15470 : return gjs_g_arg_release_internal(cx, transfer, type_info, type_tag,
3275 : 15470 : GJS_ARGUMENT_ARGUMENT, flags, arg);
3276 : : }
3277 : :
3278 : 75 : bool gjs_g_argument_release_in_arg(JSContext* cx, GITransfer transfer,
3279 : : GITypeInfo* type_info,
3280 : : GjsArgumentFlags flags, GIArgument* arg) {
3281 : : GITypeTag type_tag;
3282 : :
3283 : : /* GI_TRANSFER_EVERYTHING: we don't own the argument anymore.
3284 : : * GI_TRANSFER_CONTAINER:
3285 : : * - non-containers: treated as GI_TRANSFER_EVERYTHING
3286 : : * - containers: See FIXME in gjs_array_to_g_list(); currently
3287 : : * an error and we won't get here.
3288 : : */
3289 [ + + ]: 75 : if (transfer != GI_TRANSFER_NOTHING)
3290 : 1 : return true;
3291 : :
3292 : 74 : type_tag = g_type_info_get_tag( (GITypeInfo*) type_info);
3293 : :
3294 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3295 : : "Releasing GArgument %s in param",
3296 : : g_type_tag_to_string(type_tag));
3297 : :
3298 [ + + ]: 74 : if (type_needs_release (type_info, type_tag))
3299 : 40 : return gjs_g_arg_release_internal(cx, transfer, type_info, type_tag,
3300 : 40 : GJS_ARGUMENT_ARGUMENT, flags, arg);
3301 : :
3302 : 34 : return true;
3303 : : }
3304 : :
3305 : 323 : bool gjs_g_argument_release_in_array(JSContext* context, GITransfer transfer,
3306 : : GITypeInfo* type_info, unsigned length,
3307 : : GIArgument* arg) {
3308 [ + + ]: 323 : if (transfer != GI_TRANSFER_NOTHING)
3309 : 3 : return true;
3310 : :
3311 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3312 : : "Releasing GArgument array in param");
3313 : :
3314 : 320 : GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
3315 : : return gjs_g_argument_release_array_internal<
3316 : 320 : ArrayReleaseType::EXPLICIT_LENGTH>(context, GI_TRANSFER_EVERYTHING,
3317 : : GjsArgumentFlags::ARG_IN, param_type,
3318 : 320 : length, arg);
3319 : 320 : }
3320 : :
3321 : 12 : bool gjs_g_argument_release_in_array(JSContext* context, GITransfer transfer,
3322 : : GITypeInfo* type_info, GIArgument* arg) {
3323 [ + + ]: 12 : if (transfer != GI_TRANSFER_NOTHING)
3324 : 1 : return true;
3325 : :
3326 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3327 : : "Releasing GArgument array in param");
3328 : :
3329 : 11 : GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
3330 : : return gjs_g_argument_release_array_internal<
3331 : 11 : ArrayReleaseType::ZERO_TERMINATED>(context, GI_TRANSFER_EVERYTHING,
3332 : : GjsArgumentFlags::ARG_IN, param_type,
3333 : 11 : 0, arg);
3334 : 11 : }
3335 : :
3336 : 37 : bool gjs_g_argument_release_out_array(JSContext* context, GITransfer transfer,
3337 : : GITypeInfo* type_info, unsigned length,
3338 : : GIArgument* arg) {
3339 [ + + ]: 37 : if (transfer == GI_TRANSFER_NOTHING)
3340 : 15 : return true;
3341 : :
3342 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3343 : : "Releasing GArgument array out param");
3344 : :
3345 : 22 : GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
3346 [ + + ]: 22 : ? GI_TRANSFER_NOTHING
3347 : : : GI_TRANSFER_EVERYTHING;
3348 : :
3349 : 22 : GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
3350 : : return gjs_g_argument_release_array_internal<
3351 : 22 : ArrayReleaseType::EXPLICIT_LENGTH>(context, element_transfer,
3352 : : GjsArgumentFlags::ARG_OUT,
3353 : 22 : param_type, length, arg);
3354 : 22 : }
3355 : :
3356 : 2 : bool gjs_g_argument_release_out_array(JSContext* context, GITransfer transfer,
3357 : : GITypeInfo* type_info, GIArgument* arg) {
3358 [ + - ]: 2 : if (transfer == GI_TRANSFER_NOTHING)
3359 : 2 : return true;
3360 : :
3361 : : gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
3362 : : "Releasing GArgument array out param");
3363 : :
3364 : 0 : GITransfer element_transfer = transfer == GI_TRANSFER_CONTAINER
3365 [ # # ]: 0 : ? GI_TRANSFER_NOTHING
3366 : : : GI_TRANSFER_EVERYTHING;
3367 : :
3368 : 0 : GjsAutoTypeInfo param_type = g_type_info_get_param_type(type_info, 0);
3369 : : return gjs_g_argument_release_array_internal<
3370 : 0 : ArrayReleaseType::ZERO_TERMINATED>(context, element_transfer,
3371 : : GjsArgumentFlags::ARG_OUT,
3372 : 0 : param_type, 0, arg);
3373 : 0 : }
|