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