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 : :
5 : : #include <config.h>
6 : :
7 : : #include <stddef.h> // for NULL
8 : : #include <stdint.h>
9 : :
10 : : #include <sstream>
11 : : #include <string>
12 : :
13 : : #include <girepository.h>
14 : : #include <glib-object.h>
15 : : #include <glib.h>
16 : :
17 : : #include <js/Array.h>
18 : : #include <js/BigInt.h>
19 : : #include <js/CharacterEncoding.h>
20 : : #include <js/Conversions.h>
21 : : #include <js/Exception.h>
22 : : #include <js/GCVector.h> // for RootedVector
23 : : #include <js/Realm.h>
24 : : #include <js/RootingAPI.h>
25 : : #include <js/TypeDecls.h>
26 : : #include <js/Utility.h> // for UniqueChars
27 : : #include <js/Value.h>
28 : : #include <js/ValueArray.h>
29 : : #include <js/experimental/TypedData.h>
30 : : #include <jsapi.h> // for InformalValueTypeName, JS_Get...
31 : : #include <jsfriendapi.h> // for JS_GetObjectFunction
32 : :
33 : : #include "gi/arg-inl.h"
34 : : #include "gi/arg.h"
35 : : #include "gi/boxed.h"
36 : : #include "gi/closure.h"
37 : : #include "gi/foreign.h"
38 : : #include "gi/fundamental.h"
39 : : #include "gi/gerror.h"
40 : : #include "gi/gtype.h"
41 : : #include "gi/js-value-inl.h"
42 : : #include "gi/object.h"
43 : : #include "gi/param.h"
44 : : #include "gi/union.h"
45 : : #include "gi/value.h"
46 : : #include "gi/wrapperutils.h"
47 : : #include "gjs/byteArray.h"
48 : : #include "gjs/context-private.h"
49 : : #include "gjs/jsapi-util.h"
50 : : #include "gjs/macros.h"
51 : : #include "gjs/objectbox.h"
52 : : #include "util/log.h"
53 : :
54 : : GJS_JSAPI_RETURN_CONVENTION
55 : : static bool gjs_value_from_g_value_internal(JSContext*, JS::MutableHandleValue,
56 : : const GValue*, bool no_copy = false,
57 : : bool is_introspected_signal = false,
58 : : GIArgInfo* = nullptr,
59 : : GITypeInfo* = nullptr);
60 : :
61 : : GJS_JSAPI_RETURN_CONVENTION
62 : 7 : static bool gjs_arg_set_from_gvalue(JSContext* cx, GIArgument* arg,
63 : : const GValue* value) {
64 [ - - - - : 7 : switch (G_VALUE_TYPE(value)) {
- - - - -
- - - - -
+ ]
65 : 0 : case G_TYPE_CHAR:
66 : 0 : gjs_arg_set(arg, g_value_get_schar(value));
67 : 0 : return true;
68 : 0 : case G_TYPE_UCHAR:
69 : 0 : gjs_arg_set(arg, g_value_get_uchar(value));
70 : 0 : return true;
71 : 0 : case G_TYPE_BOOLEAN:
72 : 0 : gjs_arg_set(arg, g_value_get_boolean(value));
73 : 0 : return true;
74 : 0 : case G_TYPE_INT:
75 : 0 : gjs_arg_set(arg, g_value_get_int(value));
76 : 0 : return true;
77 : 0 : case G_TYPE_UINT:
78 : 0 : gjs_arg_set(arg, g_value_get_uint(value));
79 : 0 : return true;
80 : 0 : case G_TYPE_LONG:
81 : 0 : gjs_arg_set<long, GJS_TYPE_TAG_LONG>( // NOLINT(runtime/int)
82 : : arg, g_value_get_long(value));
83 : 0 : return true;
84 : 0 : case G_TYPE_ULONG:
85 : : gjs_arg_set<unsigned long, // NOLINT(runtime/int)
86 : 0 : GJS_TYPE_TAG_LONG>(arg, g_value_get_ulong(value));
87 : 0 : return true;
88 : 0 : case G_TYPE_INT64:
89 : 0 : gjs_arg_set(arg, int64_t{g_value_get_int64(value)});
90 : 0 : return true;
91 : 0 : case G_TYPE_UINT64:
92 : 0 : gjs_arg_set(arg, uint64_t{g_value_get_uint64(value)});
93 : 0 : return true;
94 : 0 : case G_TYPE_FLOAT:
95 : 0 : gjs_arg_set(arg, g_value_get_float(value));
96 : 0 : return true;
97 : 0 : case G_TYPE_DOUBLE:
98 : 0 : gjs_arg_set(arg, g_value_get_double(value));
99 : 0 : return true;
100 : 0 : case G_TYPE_STRING:
101 : 0 : gjs_arg_set(arg, g_value_get_string(value));
102 : 0 : return true;
103 : 0 : case G_TYPE_POINTER:
104 : 0 : gjs_arg_set(arg, g_value_get_pointer(value));
105 : 0 : return true;
106 : 0 : case G_TYPE_VARIANT:
107 : 0 : gjs_arg_set(arg, g_value_get_variant(value));
108 : 0 : return true;
109 : 7 : default: {
110 [ + - ]: 7 : if (g_value_fits_pointer(value)) {
111 : 7 : gjs_arg_set(arg, g_value_peek_pointer(value));
112 : 7 : return true;
113 : : }
114 : :
115 : 0 : GType gtype = G_VALUE_TYPE(value);
116 : :
117 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_FLAGS)) {
# # ]
118 : 0 : gjs_arg_set(arg, g_value_get_flags(value));
119 : 0 : return true;
120 : : }
121 : :
122 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_ENUM)) {
# # ]
123 : 0 : gjs_arg_set(arg, g_value_get_enum(value));
124 : 0 : return true;
125 : : }
126 : :
127 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_GTYPE)) {
# # ]
128 : 0 : gjs_arg_set<GType, GI_TYPE_TAG_GTYPE>(arg,
129 : : g_value_get_gtype(value));
130 : 0 : return true;
131 : : }
132 : :
133 [ # # # # : 0 : if (g_type_is_a(gtype, G_TYPE_PARAM)) {
# # ]
134 : 0 : gjs_arg_set(arg, g_value_get_param(value));
135 : 0 : return true;
136 : : }
137 : : }
138 : : }
139 : :
140 : 0 : gjs_throw(cx, "No know GArgument conversion for %s",
141 : : G_VALUE_TYPE_NAME(value));
142 : 0 : return false;
143 : : }
144 : :
145 : : GJS_JSAPI_RETURN_CONVENTION
146 : 7 : static bool maybe_release_signal_value(JSContext* cx,
147 : : GjsAutoArgInfo const& arg_info,
148 : : GITypeInfo* type_info,
149 : : const GValue* gvalue,
150 : : GITransfer transfer) {
151 [ - + ]: 7 : if (transfer == GI_TRANSFER_NOTHING)
152 : 0 : return true;
153 : :
154 : : GIArgument arg;
155 [ - + ]: 7 : if (!gjs_arg_set_from_gvalue(cx, &arg, gvalue))
156 : 0 : return false;
157 : :
158 [ - + ]: 7 : if (!gjs_gi_argument_release(cx, transfer, type_info,
159 : : GjsArgumentFlags::ARG_OUT, &arg)) {
160 : 0 : gjs_throw(cx, "Cannot release argument %s value, we're gonna leak!",
161 : : arg_info.name());
162 : 0 : return false;
163 : : }
164 : :
165 : 7 : return true;
166 : : }
167 : :
168 : : /*
169 : : * Gets signal introspection info about closure, or NULL if not found. Currently
170 : : * only works for signals on introspected GObjects, not signals on GJS-defined
171 : : * GObjects nor standalone closures. The return value must be unreffed.
172 : : */
173 : 8417 : [[nodiscard]] static GjsAutoSignalInfo get_signal_info_if_available(
174 : : GSignalQuery* signal_query) {
175 [ + + ]: 8417 : if (!signal_query->itype)
176 : 8046 : return nullptr;
177 : :
178 : : GjsAutoBaseInfo obj =
179 : 371 : g_irepository_find_by_gtype(nullptr, signal_query->itype);
180 [ + + ]: 371 : if (!obj)
181 : 66 : return nullptr;
182 : :
183 : 305 : GIInfoType info_type = obj.type();
184 [ + + ]: 305 : if (info_type == GI_INFO_TYPE_OBJECT)
185 : 304 : return g_object_info_find_signal(obj, signal_query->signal_name);
186 [ + - ]: 1 : else if (info_type == GI_INFO_TYPE_INTERFACE)
187 : 1 : return g_interface_info_find_signal(obj, signal_query->signal_name);
188 : :
189 : 0 : return nullptr;
190 : 371 : }
191 : :
192 : : /*
193 : : * Fill in value_p with a JS array, converted from a C array stored as a pointer
194 : : * in array_value, with its length stored in array_length_value.
195 : : */
196 : : GJS_JSAPI_RETURN_CONVENTION
197 : 1 : static bool gjs_value_from_array_and_length_values(
198 : : JSContext* context, JS::MutableHandleValue value_p,
199 : : GITypeInfo* array_type_info, const GValue* array_value,
200 : : GIArgInfo* array_length_arg_info, GITypeInfo* array_length_type_info,
201 : : const GValue* array_length_value, bool no_copy,
202 : : bool is_introspected_signal) {
203 : 1 : JS::RootedValue array_length(context);
204 : :
205 : 1 : g_assert(G_VALUE_HOLDS_POINTER(array_value));
206 : 1 : g_assert(G_VALUE_HOLDS_INT(array_length_value));
207 : :
208 [ - + ]: 1 : if (!gjs_value_from_g_value_internal(
209 : : context, &array_length, array_length_value, no_copy, is_introspected_signal,
210 : : array_length_arg_info, array_length_type_info))
211 : 0 : return false;
212 : :
213 : : GIArgument array_arg;
214 : 1 : gjs_arg_set(&array_arg, g_value_get_pointer(array_value));
215 : :
216 [ - + ]: 1 : return gjs_value_from_explicit_array(
217 : : context, value_p, array_type_info,
218 : : no_copy ? GI_TRANSFER_NOTHING : GI_TRANSFER_EVERYTHING, &array_arg,
219 : 1 : array_length.toInt32());
220 : 1 : }
221 : :
222 : : // FIXME(3v1n0): Move into closure.cpp one day...
223 : 8417 : void Gjs::Closure::marshal(GValue* return_value, unsigned n_param_values,
224 : : const GValue* param_values, void* invocation_hint,
225 : : void* marshal_data) {
226 : : JSContext *context;
227 : : unsigned i;
228 : 8417 : GSignalQuery signal_query = { 0, };
229 : :
230 : : gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Marshal closure %p", this);
231 : :
232 [ - + ]: 8417 : if (!is_valid()) {
233 : : /* We were destroyed; become a no-op */
234 : 8 : return;
235 : : }
236 : :
237 : 8417 : context = m_cx;
238 : 8417 : GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
239 [ - + ]: 8417 : if (G_UNLIKELY(gjs->sweeping())) {
240 : 0 : GSignalInvocationHint *hint = (GSignalInvocationHint*) invocation_hint;
241 : 0 : std::ostringstream message;
242 : :
243 : : message << "Attempting to call back into JSAPI during the sweeping "
244 : : "phase of GC. This is most likely caused by not destroying "
245 : : "a Clutter actor or Gtk+ widget with ::destroy signals "
246 : : "connected, but can also be caused by using the destroy(), "
247 : : "dispose(), or remove() vfuncs. Because it would crash the "
248 : : "application, it has been blocked and the JS callback not "
249 : 0 : "invoked.";
250 [ # # ]: 0 : if (hint) {
251 : : gpointer instance;
252 : 0 : g_signal_query(hint->signal_id, &signal_query);
253 : :
254 : 0 : instance = g_value_peek_pointer(¶m_values[0]);
255 : : message << "\nThe offending signal was " << signal_query.signal_name
256 : 0 : << " on " << g_type_name(G_TYPE_FROM_INSTANCE(instance))
257 : 0 : << " " << instance << ".";
258 : : }
259 : 0 : message << "\n" << gjs_dumpstack_string();
260 : 0 : g_critical("%s", message.str().c_str());
261 : 0 : return;
262 : 0 : }
263 : :
264 : 8417 : JSAutoRealm ar(context, callable());
265 : :
266 [ + + ]: 8417 : if (marshal_data) {
267 : : /* we are used for a signal handler */
268 : : guint signal_id;
269 : :
270 : 371 : signal_id = GPOINTER_TO_UINT(marshal_data);
271 : :
272 : 371 : g_signal_query(signal_id, &signal_query);
273 : :
274 [ - + ]: 371 : if (!signal_query.signal_id) {
275 : 0 : gjs_debug(GJS_DEBUG_GCLOSURE,
276 : : "Signal handler being called on invalid signal");
277 : 0 : return;
278 : : }
279 : :
280 [ - + ]: 371 : if (signal_query.n_params + 1 != n_param_values) {
281 : 0 : gjs_debug(GJS_DEBUG_GCLOSURE,
282 : : "Signal handler being called with wrong number of parameters");
283 : 0 : return;
284 : : }
285 : : }
286 : :
287 : : /* Check if any parameters, such as array lengths, need to be eliminated
288 : : * before we invoke the closure.
289 : : */
290 : : struct ArgumentDetails {
291 : : int array_len_index_for = -1;
292 : : bool skip = false;
293 : : GITypeInfo type_info;
294 : : GjsAutoArgInfo arg_info;
295 : : };
296 : 8417 : std::vector<ArgumentDetails> args_details(n_param_values);
297 : 8417 : bool needs_cleanup = false;
298 : :
299 : 8417 : GjsAutoSignalInfo signal_info = get_signal_info_if_available(&signal_query);
300 [ + + ]: 8417 : if (signal_info) {
301 : : /* Start at argument 1, skip the instance parameter */
302 [ + + ]: 693 : for (i = 1; i < n_param_values; ++i) {
303 : : int array_len_pos;
304 : :
305 : 388 : ArgumentDetails& arg_details = args_details[i];
306 : 388 : arg_details.arg_info = g_callable_info_get_arg(signal_info, i - 1);
307 : 388 : g_arg_info_load_type(arg_details.arg_info, &arg_details.type_info);
308 : :
309 : : array_len_pos =
310 : 388 : g_type_info_get_array_length(&arg_details.type_info);
311 [ + + ]: 388 : if (array_len_pos != -1) {
312 : 1 : args_details[array_len_pos + 1].skip = true;
313 : 1 : arg_details.array_len_index_for = array_len_pos + 1;
314 : : }
315 : :
316 [ + - + + : 776 : if (!needs_cleanup &&
+ + ]
317 : 388 : g_arg_info_get_ownership_transfer(arg_details.arg_info) !=
318 : : GI_TRANSFER_NOTHING)
319 : 7 : needs_cleanup = true;
320 : : }
321 : : }
322 : :
323 : 8417 : JS::RootedValueVector argv(context);
324 : : /* May end up being less */
325 [ - + ]: 8417 : if (!argv.reserve(n_param_values))
326 : 0 : g_error("Unable to reserve space");
327 : 8417 : JS::RootedValue argv_to_append(context);
328 : 8417 : bool is_introspected_signal = !!signal_info;
329 [ + + ]: 9288 : for (i = 0; i < n_param_values; ++i) {
330 : 871 : const GValue* gval = ¶m_values[i];
331 : 871 : ArgumentDetails& arg_details = args_details[i];
332 : : bool no_copy;
333 : : bool res;
334 : :
335 [ + + ]: 871 : if (arg_details.skip)
336 : 1 : continue;
337 : :
338 : 870 : no_copy = false;
339 : :
340 [ + + + + ]: 870 : if (i >= 1 && signal_query.signal_id) {
341 : 436 : no_copy = (signal_query.param_types[i - 1] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0;
342 : : }
343 : :
344 [ + + ]: 870 : if (arg_details.array_len_index_for != -1) {
345 : 1 : const GValue* array_len_gval =
346 : 1 : ¶m_values[arg_details.array_len_index_for];
347 : : ArgumentDetails& array_len_details =
348 : 1 : args_details[arg_details.array_len_index_for];
349 : 1 : res = gjs_value_from_array_and_length_values(
350 : : context, &argv_to_append, &arg_details.type_info, gval,
351 : : array_len_details.arg_info, &array_len_details.type_info,
352 : : array_len_gval, no_copy, is_introspected_signal);
353 : : } else {
354 : 869 : res = gjs_value_from_g_value_internal(
355 : : context, &argv_to_append, gval, no_copy, is_introspected_signal,
356 : : arg_details.arg_info, &arg_details.type_info);
357 : : }
358 : :
359 [ - + ]: 870 : if (!res) {
360 : 0 : gjs_debug(GJS_DEBUG_GCLOSURE,
361 : : "Unable to convert arg %d in order to invoke closure",
362 : : i);
363 : 0 : gjs_log_exception(context);
364 : 0 : return;
365 : : }
366 : :
367 : 870 : argv.infallibleAppend(argv_to_append);
368 : : }
369 : :
370 : 8417 : JS::RootedValue rval(context);
371 : :
372 [ + + ]: 8417 : if (!invoke(nullptr, argv, &rval)) {
373 [ - + ]: 1 : if (JS_IsExceptionPending(context)) {
374 : 0 : gjs_log_exception_uncaught(context);
375 : : } else {
376 : : // "Uncatchable" exception thrown, we have to exit. This
377 : : // matches the closure exit handling in function.cpp
378 : : uint8_t code;
379 [ + - ]: 1 : if (gjs->should_exit(&code))
380 : 1 : gjs->exit_immediately(code);
381 : :
382 : : // Some other uncatchable exception, e.g. out of memory
383 : 0 : JSFunction* fn = JS_GetObjectFunction(callable());
384 : : std::string descr =
385 : 0 : fn ? "function " + gjs_debug_string(JS_GetFunctionDisplayId(fn))
386 [ # # # # : 0 : : "callable object " + gjs_debug_object(callable());
# # ]
387 : 0 : g_error("Call to %s terminated with uncatchable exception",
388 : : descr.c_str());
389 : : }
390 : : }
391 : :
392 [ + + ]: 8416 : if (needs_cleanup) {
393 [ + + ]: 21 : for (i = 0; i < n_param_values; ++i) {
394 : 14 : ArgumentDetails& arg_details = args_details[i];
395 [ + + ]: 14 : if (!arg_details.arg_info)
396 : 7 : continue;
397 : :
398 : : GITransfer transfer =
399 : 7 : g_arg_info_get_ownership_transfer(arg_details.arg_info);
400 : :
401 [ - + ]: 7 : if (transfer == GI_TRANSFER_NOTHING)
402 : 0 : continue;
403 : :
404 [ - + ]: 7 : if (!maybe_release_signal_value(context, arg_details.arg_info,
405 : : &arg_details.type_info,
406 : 7 : ¶m_values[i], transfer)) {
407 : 0 : gjs_log_exception(context);
408 : 0 : return;
409 : : }
410 : : }
411 : : }
412 : :
413 : : // null return_value means the closure wasn't expected to return a value.
414 : : // Discard the JS function's return value in that case.
415 [ + + ]: 8416 : if (return_value != NULL) {
416 [ + + ]: 8022 : if (rval.isUndefined()) {
417 : : // Either an exception was thrown and logged, or the JS function
418 : : // returned undefined. Leave the GValue uninitialized.
419 : : // FIXME: not sure what happens on the other side with an
420 : : // uninitialized GValue!
421 : 4 : return;
422 : : }
423 : :
424 [ + + ]: 8018 : if (!gjs_value_to_g_value(context, rval, return_value)) {
425 : 4 : gjs_debug(GJS_DEBUG_GCLOSURE,
426 : : "Unable to convert return value when invoking closure");
427 : 4 : gjs_log_exception(context);
428 : 4 : return;
429 : : }
430 : : }
431 [ + + + + : 8456 : }
+ + + + +
+ + + ]
432 : :
433 : : GJS_JSAPI_RETURN_CONVENTION
434 : 76 : static bool gjs_value_guess_g_type(JSContext* context, JS::Value value,
435 : : GType* gtype_out) {
436 : 76 : g_assert(gtype_out && "Invalid return location");
437 : :
438 [ + + ]: 76 : if (value.isNull()) {
439 : 1 : *gtype_out = G_TYPE_POINTER;
440 : 1 : return true;
441 : : }
442 [ + + ]: 75 : if (value.isString()) {
443 : 6 : *gtype_out = G_TYPE_STRING;
444 : 6 : return true;
445 : : }
446 [ + + ]: 69 : if (value.isInt32()) {
447 : 22 : *gtype_out = G_TYPE_INT;
448 : 22 : return true;
449 : : }
450 [ + + ]: 47 : if (value.isDouble()) {
451 : 5 : *gtype_out = G_TYPE_DOUBLE;
452 : 5 : return true;
453 : : }
454 [ + + ]: 42 : if (value.isBoolean()) {
455 : 2 : *gtype_out = G_TYPE_BOOLEAN;
456 : 2 : return true;
457 : : }
458 [ + + ]: 40 : if (value.isBigInt()) {
459 : : // Assume that if the value is negative or within the int64_t limit,
460 : : // then we're handling a signed integer, otherwise unsigned.
461 : : int64_t ignored;
462 [ + + + + : 5 : if (JS::BigIntIsNegative(value.toBigInt()) ||
+ + ]
463 : 2 : JS::BigIntFits(value.toBigInt(), &ignored))
464 : 2 : *gtype_out = G_TYPE_INT64;
465 : : else
466 : 1 : *gtype_out = G_TYPE_UINT64;
467 : 3 : return true;
468 : : }
469 [ + - ]: 37 : if (value.isObject()) {
470 : 37 : JS::RootedObject obj(context, &value.toObject());
471 : 37 : return gjs_gtype_get_actual_gtype(context, obj, gtype_out);
472 : 37 : }
473 : :
474 : 0 : *gtype_out = G_TYPE_INVALID;
475 : 0 : return true;
476 : : }
477 : :
478 : 27 : static bool throw_expect_type(JSContext* cx, JS::HandleValue value,
479 : : const char* expected_type, GType gtype = 0,
480 : : bool out_of_range = false) {
481 : 27 : JS::UniqueChars val_str;
482 [ + + + - ]: 27 : out_of_range = (out_of_range && value.isNumeric());
483 : :
484 [ + + ]: 27 : if (out_of_range) {
485 : 7 : JS::RootedString str(cx, JS::ToString(cx, value));
486 [ + - ]: 7 : if (str)
487 : 7 : val_str = JS_EncodeStringToUTF8(cx, str);
488 : 7 : }
489 : :
490 [ + + + + : 52 : gjs_throw(cx, "Wrong type %s; %s%s%s expected%s%s",
+ + + + ]
491 : : JS::InformalValueTypeName(value), expected_type, gtype ? " " : "",
492 : 18 : gtype ? g_type_name(gtype) : "",
493 : : out_of_range ? ". But it's out of range: " : "",
494 : 7 : out_of_range ? val_str.get() : "");
495 : 54 : return false; /* for convenience */
496 : 27 : }
497 : :
498 : : GJS_JSAPI_RETURN_CONVENTION
499 : : static bool
500 : 8966 : gjs_value_to_g_value_internal(JSContext *context,
501 : : JS::HandleValue value,
502 : : GValue *gvalue,
503 : : bool no_copy)
504 : : {
505 : : GType gtype;
506 : 8966 : bool out_of_range = false;
507 : :
508 : 8966 : gtype = G_VALUE_TYPE(gvalue);
509 : :
510 [ + + ]: 8966 : if (value.isObject()) {
511 : 230 : JS::RootedObject obj(context, &value.toObject());
512 : : GType boxed_gtype;
513 : :
514 [ - + ]: 230 : if (!gjs_gtype_get_actual_gtype(context, obj, &boxed_gtype))
515 : 0 : return false;
516 : :
517 : : // Don't unbox GValue if the GValue's gtype is GObject.Value
518 [ + + - + : 230 : if (g_type_is_a(boxed_gtype, G_TYPE_VALUE) && gtype != G_TYPE_VALUE) {
+ + + + ]
519 [ - + ]: 4 : if (no_copy) {
520 : 0 : gjs_throw(
521 : : context,
522 : : "Cannot convert GObject.Value object without copying.");
523 : 0 : return false;
524 : : }
525 : :
526 : 4 : GValue* source = BoxedBase::to_c_ptr<GValue>(context, obj);
527 : : // Only initialize the value if it doesn't have a type
528 : : // and our source GValue has been initialized
529 : 4 : GType source_gtype = G_VALUE_TYPE(source);
530 [ + - ]: 4 : if (gtype == 0) {
531 [ + + ]: 4 : if (source_gtype == 0) {
532 : 1 : gjs_throw(context,
533 : : "GObject.Value is not initialized with a type");
534 : 1 : return false;
535 : : }
536 : 3 : g_value_init(gvalue, source_gtype);
537 : : }
538 : :
539 : 3 : GType dest_gtype = G_VALUE_TYPE(gvalue);
540 [ - + ]: 3 : if (!g_value_type_compatible(source_gtype, dest_gtype)) {
541 : 0 : gjs_throw(context, "GObject.Value expected GType %s, found %s",
542 : : g_type_name(dest_gtype), g_type_name(source_gtype));
543 : 0 : return false;
544 : : }
545 : :
546 : 3 : g_value_copy(source, gvalue);
547 : 3 : return true;
548 : : }
549 [ + + ]: 230 : }
550 : :
551 [ + + ]: 8962 : if (gtype == 0) {
552 [ - + ]: 66 : if (!gjs_value_guess_g_type(context, value, >ype))
553 : 0 : return false;
554 : :
555 [ - + ]: 66 : if (gtype == G_TYPE_INVALID) {
556 : 0 : gjs_throw(context, "Could not guess unspecified GValue type");
557 : 0 : return false;
558 : : }
559 : :
560 : : gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
561 : : "Guessed GValue type %s from JS Value",
562 : : g_type_name(gtype));
563 : :
564 : 66 : g_value_init(gvalue, gtype);
565 : : }
566 : :
567 : : gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
568 : : "Converting JS::Value to gtype %s",
569 : : g_type_name(gtype));
570 : :
571 : :
572 [ + + ]: 8962 : if (gtype == G_TYPE_STRING) {
573 : : /* Don't use ValueToString since we don't want to just toString()
574 : : * everything automatically
575 : : */
576 [ + + ]: 109 : if (value.isNull()) {
577 : 3 : g_value_set_string(gvalue, NULL);
578 [ + + ]: 106 : } else if (value.isString()) {
579 : 104 : JS::RootedString str(context, value.toString());
580 : 104 : JS::UniqueChars utf8_string(JS_EncodeStringToUTF8(context, str));
581 [ - + ]: 104 : if (!utf8_string)
582 : 0 : return false;
583 : :
584 : 104 : g_value_set_string(gvalue, utf8_string.get());
585 [ + - + - ]: 104 : } else {
586 : 2 : return throw_expect_type(context, value, "string");
587 : : }
588 [ + + ]: 8853 : } else if (gtype == G_TYPE_CHAR) {
589 : : int32_t i;
590 : 16 : if (Gjs::js_value_to_c_checked<signed char>(context, value, &i,
591 [ + - + - ]: 32 : &out_of_range) &&
592 [ + - ]: 16 : !out_of_range) {
593 : 16 : g_value_set_schar(gvalue, static_cast<signed char>(i));
594 : : } else {
595 : 0 : return throw_expect_type(context, value, "char", 0, out_of_range);
596 : : }
597 [ + + ]: 8837 : } else if (gtype == G_TYPE_UCHAR) {
598 : : uint32_t i;
599 : 16 : if (Gjs::js_value_to_c_checked<unsigned char>(context, value, &i,
600 [ + - + - ]: 32 : &out_of_range) &&
601 [ + - ]: 16 : !out_of_range) {
602 : 16 : g_value_set_uchar(gvalue, (unsigned char)i);
603 : : } else {
604 : 0 : return throw_expect_type(context, value, "unsigned char", 0,
605 : 0 : out_of_range);
606 : : }
607 [ + + ]: 8821 : } else if (gtype == G_TYPE_INT) {
608 : : gint32 i;
609 [ + - ]: 178 : if (Gjs::js_value_to_c(context, value, &i)) {
610 : 178 : g_value_set_int(gvalue, i);
611 : : } else {
612 : 0 : return throw_expect_type(context, value, "integer");
613 : : }
614 [ + + ]: 8643 : } else if (gtype == G_TYPE_INT64) {
615 : : int64_t i;
616 : 35 : if (Gjs::js_value_to_c_checked<int64_t>(context, value, &i,
617 [ + - + + ]: 70 : &out_of_range) &&
618 [ + + ]: 35 : !out_of_range) {
619 : 30 : g_value_set_int64(gvalue, i);
620 : : } else {
621 : 5 : return throw_expect_type(context, value, "64-bit integer", 0,
622 : 5 : out_of_range);
623 : : }
624 [ + + ]: 8608 : } else if (gtype == G_TYPE_DOUBLE) {
625 : : gdouble d;
626 [ + - ]: 84 : if (Gjs::js_value_to_c(context, value, &d)) {
627 : 84 : g_value_set_double(gvalue, d);
628 : : } else {
629 : 0 : return throw_expect_type(context, value, "double");
630 : : }
631 [ + + ]: 8524 : } else if (gtype == G_TYPE_FLOAT) {
632 : : gdouble d;
633 : 79 : if (Gjs::js_value_to_c_checked<float>(context, value, &d,
634 [ + - + - ]: 158 : &out_of_range) &&
635 [ + - ]: 79 : !out_of_range) {
636 : 79 : g_value_set_float(gvalue, d);
637 : : } else {
638 : 0 : return throw_expect_type(context, value, "float", 0, out_of_range);
639 : : }
640 [ + + ]: 8445 : } else if (gtype == G_TYPE_UINT) {
641 : : guint32 i;
642 [ + - ]: 17 : if (Gjs::js_value_to_c(context, value, &i)) {
643 : 17 : g_value_set_uint(gvalue, i);
644 : : } else {
645 : 0 : return throw_expect_type(context, value, "unsigned integer");
646 : : }
647 [ + + ]: 8428 : } else if (gtype == G_TYPE_UINT64) {
648 : : uint64_t i;
649 : 22 : if (Gjs::js_value_to_c_checked<uint64_t>(context, value, &i,
650 [ + - + + ]: 44 : &out_of_range) &&
651 [ + + ]: 22 : !out_of_range) {
652 : 20 : g_value_set_uint64(gvalue, i);
653 : : } else {
654 : 2 : return throw_expect_type(context, value, "unsigned 64-bit integer",
655 : 2 : 0, out_of_range);
656 : : }
657 [ + + ]: 8406 : } else if (gtype == G_TYPE_BOOLEAN) {
658 : : /* JS::ToBoolean() can't fail */
659 : 8017 : g_value_set_boolean(gvalue, JS::ToBoolean(value));
660 [ + + + + : 730 : } else if (g_type_is_a(gtype, G_TYPE_OBJECT) ||
+ + ]
661 [ + - - + ]: 341 : g_type_is_a(gtype, G_TYPE_INTERFACE)) {
662 : : GObject *gobj;
663 : :
664 : 48 : gobj = NULL;
665 [ + + ]: 48 : if (value.isNull()) {
666 : : /* nothing to do */
667 [ + - ]: 45 : } else if (value.isObject()) {
668 : 45 : JS::RootedObject obj(context, &value.toObject());
669 [ + - ]: 90 : if (!ObjectBase::typecheck(context, obj, nullptr, gtype) ||
670 [ - + - + ]: 90 : !ObjectBase::to_c_ptr(context, obj, &gobj))
671 : 0 : return false;
672 [ - + ]: 45 : if (!gobj)
673 : 0 : return true; // treat disposed object as if value.isNull()
674 [ + - ]: 45 : } else {
675 : 0 : return throw_expect_type(context, value, "object", gtype);
676 : : }
677 : :
678 : 48 : g_value_set_object(gvalue, gobj);
679 [ + + ]: 341 : } else if (gtype == G_TYPE_STRV) {
680 [ - + ]: 9 : if (value.isNull())
681 : 0 : return true;
682 : :
683 : : bool is_array;
684 [ - + ]: 9 : if (!JS::IsArrayObject(context, value, &is_array))
685 : 0 : return false;
686 [ - + ]: 9 : if (!is_array)
687 : 0 : return throw_expect_type(context, value, "strv");
688 : :
689 : 9 : JS::RootedObject array_obj(context, &value.toObject());
690 : : uint32_t length;
691 [ - + ]: 9 : if (!JS::GetArrayLength(context, array_obj, &length))
692 : 0 : return throw_expect_type(context, value, "strv");
693 : :
694 : : void* result;
695 [ - + ]: 9 : if (!gjs_array_to_strv(context, value, length, &result))
696 : 0 : return false;
697 : :
698 : 9 : g_value_take_boxed(gvalue, static_cast<char**>(result));
699 [ + - + - : 341 : } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
+ + + + ]
700 : : void *gboxed;
701 : :
702 : 149 : gboxed = NULL;
703 [ + + ]: 149 : if (value.isNull())
704 : 23 : return true;
705 : :
706 : : /* special case GValue */
707 [ + + ]: 126 : if (gtype == G_TYPE_VALUE) {
708 : : /* explicitly handle values that are already GValues
709 : : to avoid infinite recursion */
710 [ + + ]: 18 : if (value.isObject()) {
711 : 10 : JS::RootedObject obj(context, &value.toObject());
712 : : GType guessed_gtype;
713 : :
714 [ - + ]: 10 : if (!gjs_value_guess_g_type(context, value, &guessed_gtype))
715 : 0 : return false;
716 : :
717 [ + - ]: 10 : if (guessed_gtype == G_TYPE_VALUE) {
718 : 10 : gboxed = BoxedBase::to_c_ptr<GValue>(context, obj);
719 : 10 : g_value_set_boxed(gvalue, gboxed);
720 : 10 : return true;
721 : : }
722 [ - + ]: 10 : }
723 : :
724 : 8 : Gjs::AutoGValue nested_gvalue;
725 [ - + ]: 8 : if (!gjs_value_to_g_value(context, value, &nested_gvalue))
726 : 0 : return false;
727 : :
728 : 8 : g_value_set_boxed(gvalue, &nested_gvalue);
729 : 8 : return true;
730 : 8 : }
731 : :
732 [ + + ]: 108 : if (value.isObject()) {
733 : 90 : JS::RootedObject obj(context, &value.toObject());
734 : :
735 [ + + ]: 90 : if (gtype == ObjectBox::gtype()) {
736 : 35 : g_value_set_boxed(gvalue, ObjectBox::boxed(context, obj).get());
737 : 35 : return true;
738 [ + + ]: 55 : } else if (gtype == G_TYPE_ERROR) {
739 : : /* special case GError */
740 : 1 : gboxed = ErrorBase::to_c_ptr(context, obj);
741 [ - + ]: 1 : if (!gboxed)
742 : 0 : return false;
743 [ + + ]: 54 : } else if (gtype == G_TYPE_BYTE_ARRAY) {
744 : : /* special case GByteArray */
745 : 9 : JS::RootedObject obj(context, &value.toObject());
746 [ + - ]: 9 : if (JS_IsUint8Array(obj)) {
747 : 9 : g_value_take_boxed(gvalue,
748 : 9 : gjs_byte_array_get_byte_array(obj));
749 : 9 : return true;
750 : : }
751 [ - + - + ]: 54 : } else if (gtype == G_TYPE_ARRAY) {
752 : 0 : gjs_throw(context, "Converting %s to GArray is not supported",
753 : : JS::InformalValueTypeName(value));
754 : 0 : return false;
755 [ - + ]: 45 : } else if (gtype == G_TYPE_PTR_ARRAY) {
756 : 0 : gjs_throw(context, "Converting %s to GArray is not supported",
757 : : JS::InformalValueTypeName(value));
758 : 0 : return false;
759 [ - + ]: 45 : } else if (gtype == G_TYPE_HASH_TABLE) {
760 : 0 : gjs_throw(context,
761 : : "Converting %s to GHashTable is not supported",
762 : : JS::InformalValueTypeName(value));
763 : 0 : return false;
764 : : } else {
765 : : GjsAutoBaseInfo registered =
766 : 45 : g_irepository_find_by_gtype(nullptr, gtype);
767 : :
768 : : /* We don't necessarily have the typelib loaded when
769 : : we first see the structure... */
770 [ + - ]: 45 : if (registered) {
771 : 45 : GIInfoType info_type = registered.type();
772 : :
773 [ + + + + : 89 : if (info_type == GI_INFO_TYPE_STRUCT &&
+ + ]
774 : 44 : g_struct_info_is_foreign(
775 : : registered.as<GIStructInfo>())) {
776 : : GIArgument arg;
777 : :
778 [ - + ]: 2 : if (!gjs_struct_foreign_convert_to_gi_argument(
779 : : context, value, registered, nullptr,
780 : : GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING,
781 : : GjsArgumentFlags::MAY_BE_NULL, &arg))
782 : 0 : return false;
783 : :
784 : 2 : gboxed = gjs_arg_get<void*>(&arg);
785 : : }
786 : : }
787 : :
788 : : /* First try a union, if that fails,
789 : : assume a boxed struct. Distinguishing
790 : : which one is expected would require checking
791 : : the associated GIBaseInfo, which is not necessary
792 : : possible, if e.g. we see the GType without
793 : : loading the typelib.
794 : : */
795 [ + + ]: 45 : if (!gboxed) {
796 [ + + ]: 43 : if (UnionBase::typecheck(context, obj, nullptr, gtype,
797 : : GjsTypecheckNoThrow())) {
798 : 1 : gboxed = UnionBase::to_c_ptr(context, obj);
799 : : } else {
800 [ - + ]: 42 : if (!BoxedBase::typecheck(context, obj, nullptr, gtype))
801 : 0 : return false;
802 : :
803 : 42 : gboxed = BoxedBase::to_c_ptr(context, obj);
804 : : }
805 [ - + ]: 43 : if (!gboxed)
806 : 0 : return false;
807 : : }
808 [ + - ]: 45 : }
809 [ + + ]: 90 : } else {
810 : 18 : return throw_expect_type(context, value, "boxed type", gtype);
811 : : }
812 : :
813 [ + + ]: 46 : if (no_copy)
814 : 2 : g_value_set_static_boxed(gvalue, gboxed);
815 : : else
816 : 44 : g_value_set_boxed(gvalue, gboxed);
817 [ + + ]: 183 : } else if (gtype == G_TYPE_VARIANT) {
818 : 45 : GVariant *variant = NULL;
819 : :
820 [ + + ]: 45 : if (value.isNull()) {
821 : : /* nothing to do */
822 [ + - ]: 43 : } else if (value.isObject()) {
823 : 43 : JS::RootedObject obj(context, &value.toObject());
824 : :
825 [ - + ]: 43 : if (!BoxedBase::typecheck(context, obj, nullptr, G_TYPE_VARIANT))
826 : 0 : return false;
827 : :
828 : 43 : variant = BoxedBase::to_c_ptr<GVariant>(context, obj);
829 [ - + ]: 43 : if (!variant)
830 : 0 : return false;
831 [ + - ]: 43 : } else {
832 : 0 : return throw_expect_type(context, value, "boxed type", gtype);
833 : : }
834 : :
835 : 45 : g_value_set_variant (gvalue, variant);
836 [ + - + + : 138 : } else if (g_type_is_a(gtype, G_TYPE_ENUM)) {
+ + ]
837 : : int64_t value_int64;
838 : :
839 [ + - ]: 59 : if (Gjs::js_value_to_c(context, value, &value_int64)) {
840 : : GEnumValue *v;
841 : 59 : GjsAutoTypeClass<GEnumClass> enum_class(gtype);
842 : :
843 : : /* See arg.c:_gjs_enum_to_int() */
844 : 59 : v = g_enum_get_value(enum_class, (int)value_int64);
845 [ - + ]: 59 : if (v == NULL) {
846 : 0 : gjs_throw(context,
847 : : "%d is not a valid value for enumeration %s",
848 : : value.toInt32(), g_type_name(gtype));
849 : 0 : return false;
850 : : }
851 : :
852 : 59 : g_value_set_enum(gvalue, v->value);
853 [ + - ]: 59 : } else {
854 : 0 : return throw_expect_type(context, value, "enum", gtype);
855 : : }
856 [ + - + + : 79 : } else if (g_type_is_a(gtype, G_TYPE_FLAGS)) {
+ + ]
857 : : int64_t value_int64;
858 : :
859 [ + - ]: 13 : if (Gjs::js_value_to_c(context, value, &value_int64)) {
860 [ - + ]: 13 : if (!_gjs_flags_value_is_valid(context, gtype, value_int64))
861 : 0 : return false;
862 : :
863 : : /* See arg.c:_gjs_enum_to_int() */
864 : 13 : g_value_set_flags(gvalue, (int)value_int64);
865 : : } else {
866 : 0 : return throw_expect_type(context, value, "flags", gtype);
867 : : }
868 [ + + - + : 66 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
869 : : void *gparam;
870 : :
871 : 2 : gparam = NULL;
872 [ + - ]: 2 : if (value.isNull()) {
873 : : /* nothing to do */
874 [ + - ]: 2 : } else if (value.isObject()) {
875 : 2 : JS::RootedObject obj(context, &value.toObject());
876 : :
877 [ - + ]: 2 : if (!gjs_typecheck_param(context, obj, gtype, true))
878 : 0 : return false;
879 : :
880 : 2 : gparam = gjs_g_param_from_param(context, obj);
881 [ + - ]: 2 : } else {
882 : 0 : return throw_expect_type(context, value, "param type", gtype);
883 : : }
884 : :
885 : 2 : g_value_set_param(gvalue, (GParamSpec*) gparam);
886 [ + + ]: 64 : } else if (gtype == G_TYPE_GTYPE) {
887 : : GType type;
888 : :
889 [ - + ]: 10 : if (!value.isObject())
890 : 0 : return throw_expect_type(context, value, "GType object");
891 : :
892 : 10 : JS::RootedObject obj(context, &value.toObject());
893 [ - + ]: 10 : if (!gjs_gtype_get_actual_gtype(context, obj, &type))
894 : 0 : return false;
895 : 10 : g_value_set_gtype(gvalue, type);
896 [ + - + + : 64 : } else if (g_type_is_a(gtype, G_TYPE_POINTER)) {
- + + + ]
897 [ - + ]: 11 : if (value.isNull()) {
898 : : /* Nothing to do */
899 : : } else {
900 : 0 : gjs_throw(context,
901 : : "Cannot convert non-null JS value to G_POINTER");
902 : 0 : return false;
903 : : }
904 [ + + + - : 75 : } else if (value.isNumber() &&
+ + ]
905 : 32 : g_value_type_transformable(G_TYPE_INT, gtype)) {
906 : : /* Only do this crazy gvalue transform stuff after we've
907 : : * exhausted everything else. Adding this for
908 : : * e.g. ClutterUnit.
909 : : */
910 : : gint32 i;
911 [ + - ]: 32 : if (Gjs::js_value_to_c(context, value, &i)) {
912 : 32 : GValue int_value = { 0, };
913 : 32 : g_value_init(&int_value, G_TYPE_INT);
914 : 32 : g_value_set_int(&int_value, i);
915 : 32 : g_value_transform(&int_value, gvalue);
916 : : } else {
917 : 0 : return throw_expect_type(context, value, "integer");
918 : : }
919 [ + - ]: 11 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
920 : : // The gtype is none of the above, it should be derived from a custom
921 : : // fundamental type.
922 [ - + ]: 11 : if (!value.isObject())
923 : 2 : return throw_expect_type(context, value, "object", gtype);
924 : :
925 : 11 : JS::RootedObject fundamental_object(context, &value.toObject());
926 [ + + ]: 11 : if (!FundamentalBase::to_gvalue(context, fundamental_object, gvalue))
927 : 2 : return false;
928 [ + + ]: 11 : } else {
929 : 0 : gjs_debug(GJS_DEBUG_GCLOSURE, "JS::Value is number %d gtype fundamental %d transformable to int %d from int %d",
930 : 0 : value.isNumber(),
931 : : G_TYPE_IS_FUNDAMENTAL(gtype),
932 : : g_value_type_transformable(gtype, G_TYPE_INT),
933 : : g_value_type_transformable(G_TYPE_INT, gtype));
934 : :
935 : 0 : gjs_throw(context,
936 : : "Don't know how to convert JavaScript object to GType %s",
937 : : g_type_name(gtype));
938 : 0 : return false;
939 : : }
940 : :
941 : 8848 : return true;
942 : : }
943 : :
944 : : bool
945 : 8960 : gjs_value_to_g_value(JSContext *context,
946 : : JS::HandleValue value,
947 : : GValue *gvalue)
948 : : {
949 : 8960 : return gjs_value_to_g_value_internal(context, value, gvalue, false);
950 : : }
951 : :
952 : : bool
953 : 6 : gjs_value_to_g_value_no_copy(JSContext *context,
954 : : JS::HandleValue value,
955 : : GValue *gvalue)
956 : : {
957 : 6 : return gjs_value_to_g_value_internal(context, value, gvalue, true);
958 : : }
959 : :
960 : 9 : [[nodiscard]] static JS::Value convert_int_to_enum(GType gtype, int v) {
961 : : double v_double;
962 : :
963 [ + + + - ]: 9 : if (v > 0 && v < G_MAXINT) {
964 : : /* Optimize the unambiguous case */
965 : 8 : v_double = v;
966 : : } else {
967 : : /* Need to distinguish between negative integers and unsigned integers */
968 : 1 : GjsAutoEnumInfo info = g_irepository_find_by_gtype(nullptr, gtype);
969 : :
970 : : // Native enums don't have type info, assume
971 : : // they are signed to avoid crashing when
972 : : // they are exposed to JS.
973 [ + - ]: 1 : if (!info) {
974 : 1 : v_double = int64_t(v);
975 : : } else {
976 : 0 : v_double = _gjs_enum_from_int(info, v);
977 : : }
978 : 1 : }
979 : :
980 : 9 : return JS::NumberValue(v_double);
981 : : }
982 : :
983 : : GJS_JSAPI_RETURN_CONVENTION
984 : 1718 : static bool gjs_value_from_g_value_internal(JSContext* context,
985 : : JS::MutableHandleValue value_p,
986 : : const GValue* gvalue, bool no_copy,
987 : : bool is_introspected_signal,
988 : : GIArgInfo* arg_info,
989 : : GITypeInfo* type_info) {
990 : : GType gtype;
991 : :
992 : 1718 : gtype = G_VALUE_TYPE(gvalue);
993 : :
994 : : gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
995 : : "Converting gtype %s to JS::Value",
996 : : g_type_name(gtype));
997 : :
998 [ + + + + : 3035 : if (gtype != G_TYPE_STRV && g_value_fits_pointer(gvalue) &&
+ + + + ]
999 : 1317 : g_value_peek_pointer(gvalue) == nullptr) {
1000 : : // In theory here we should throw if !g_arg_info_may_be_null(arg_info)
1001 : : // however most signals don't explicitly mark themselves as nullable,
1002 : : // so better to avoid this.
1003 : : gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
1004 : : "Converting NULL %s to JS::NullValue()",
1005 : : g_type_name(gtype));
1006 : 93 : value_p.setNull();
1007 : 93 : return true;
1008 : : }
1009 : :
1010 [ + + ]: 1625 : if (gtype == G_TYPE_STRING) {
1011 : 282 : return gjs_string_from_utf8(context, g_value_get_string(gvalue),
1012 : 282 : value_p);
1013 [ + + ]: 1343 : } else if (gtype == G_TYPE_CHAR) {
1014 : : signed char v;
1015 : 20 : v = g_value_get_schar(gvalue);
1016 : 20 : value_p.setInt32(v);
1017 [ + + ]: 1323 : } else if (gtype == G_TYPE_UCHAR) {
1018 : : unsigned char v;
1019 : 14 : v = g_value_get_uchar(gvalue);
1020 : 14 : value_p.setInt32(v);
1021 [ + + ]: 1309 : } else if (gtype == G_TYPE_INT) {
1022 : : int v;
1023 : 161 : v = g_value_get_int(gvalue);
1024 : 161 : value_p.set(JS::NumberValue(v));
1025 [ + + ]: 1148 : } else if (gtype == G_TYPE_UINT) {
1026 : : guint v;
1027 : 17 : v = g_value_get_uint(gvalue);
1028 : 17 : value_p.setNumber(v);
1029 [ + + ]: 1131 : } else if (gtype == G_TYPE_DOUBLE) {
1030 : : double d;
1031 : 17 : d = g_value_get_double(gvalue);
1032 : 17 : value_p.setNumber(JS::CanonicalizeNaN(d));
1033 [ + + ]: 1114 : } else if (gtype == G_TYPE_FLOAT) {
1034 : : double d;
1035 : 15 : d = g_value_get_float(gvalue);
1036 : 15 : value_p.setNumber(JS::CanonicalizeNaN(d));
1037 [ + + ]: 1099 : } else if (gtype == G_TYPE_BOOLEAN) {
1038 : : bool v;
1039 : 40 : v = g_value_get_boolean(gvalue);
1040 : 40 : value_p.setBoolean(!!v);
1041 [ + + + + : 1059 : } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+ - - + +
+ ]
1042 : 525 : return ObjectInstance::set_value_from_gobject(
1043 : 525 : context, static_cast<GObject*>(g_value_get_object(gvalue)),
1044 : 525 : value_p);
1045 [ + + ]: 534 : } else if (gtype == G_TYPE_STRV) {
1046 [ - + ]: 15 : if (!gjs_array_from_strv (context,
1047 : : value_p,
1048 : 15 : (const char**) g_value_get_boxed (gvalue))) {
1049 : 0 : gjs_throw(context, "Failed to convert strv to array");
1050 : 0 : return false;
1051 : : }
1052 [ + - + + ]: 519 : } else if (gtype == G_TYPE_ARRAY || gtype == G_TYPE_BYTE_ARRAY ||
1053 [ + + ]: 510 : gtype == G_TYPE_PTR_ARRAY) {
1054 [ + + ]: 15 : if (gtype == G_TYPE_BYTE_ARRAY) {
1055 : 9 : auto* byte_array = static_cast<GByteArray*>(g_value_get_boxed(gvalue));
1056 : : JSObject* array =
1057 : 9 : gjs_byte_array_from_byte_array(context, byte_array);
1058 [ - + ]: 9 : if (!array) {
1059 : 0 : gjs_throw(context,
1060 : : "Couldn't convert GByteArray to a Uint8Array");
1061 : 9 : return false;
1062 : : }
1063 : 9 : value_p.setObject(*array);
1064 : 9 : return true;
1065 : : }
1066 : :
1067 [ + - - + ]: 6 : if (!is_introspected_signal || !arg_info) {
1068 : 0 : gjs_throw(context, "Unknown signal");
1069 : 0 : return false;
1070 : : }
1071 : :
1072 : 6 : GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
1073 : 6 : GjsAutoTypeInfo element_info = g_type_info_get_param_type(type_info, 0);
1074 [ - + ]: 6 : if (!gjs_array_from_g_value_array(context, value_p, element_info,
1075 : : transfer, gvalue)) {
1076 : 0 : gjs_throw(context, "Failed to convert array");
1077 : 0 : return false;
1078 : : }
1079 [ + - + + ]: 516 : } else if (gtype == G_TYPE_HASH_TABLE) {
1080 [ - + ]: 3 : if (!arg_info) {
1081 : 0 : gjs_throw(context, "Failed to get GValue from Hash Table without"
1082 : : "signal information");
1083 : 0 : return false;
1084 : : }
1085 : 3 : GjsAutoTypeInfo key_info = g_type_info_get_param_type(type_info, 0);
1086 : 3 : GjsAutoTypeInfo value_info = g_type_info_get_param_type(type_info, 1);
1087 : 3 : GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
1088 : :
1089 [ - + ]: 3 : if (!gjs_object_from_g_hash(
1090 : : context, value_p, key_info, value_info, transfer,
1091 : 3 : static_cast<GHashTable*>(g_value_get_boxed(gvalue)))) {
1092 : 0 : gjs_throw(context, "Failed to convert Hash Table");
1093 : 0 : return false;
1094 : : }
1095 [ + - + - : 504 : } else if (g_type_is_a(gtype, G_TYPE_BOXED) || gtype == G_TYPE_VARIANT) {
+ - + + +
+ + + ]
1096 : : void *gboxed;
1097 : : JSObject *obj;
1098 : :
1099 [ + - + + : 205 : if (g_type_is_a(gtype, G_TYPE_BOXED))
+ + ]
1100 : 121 : gboxed = g_value_get_boxed(gvalue);
1101 : : else
1102 : 84 : gboxed = g_value_get_variant(gvalue);
1103 : :
1104 [ + + ]: 205 : if (gtype == ObjectBox::gtype()) {
1105 : 30 : obj = ObjectBox::object_for_c_ptr(context,
1106 : : static_cast<ObjectBox*>(gboxed));
1107 [ - + ]: 30 : if (!obj)
1108 : 41 : return false;
1109 : 30 : value_p.setObject(*obj);
1110 : 30 : return true;
1111 : : }
1112 : :
1113 : : /* special case GError */
1114 [ + + ]: 175 : if (gtype == G_TYPE_ERROR) {
1115 : 2 : obj = ErrorInstance::object_for_c_ptr(context,
1116 : : static_cast<GError*>(gboxed));
1117 [ - + ]: 2 : if (!obj)
1118 : 0 : return false;
1119 : 2 : value_p.setObject(*obj);
1120 : 2 : return true;
1121 : : }
1122 : :
1123 : : /* special case GValue */
1124 [ + + ]: 173 : if (gtype == G_TYPE_VALUE) {
1125 : 8 : return gjs_value_from_g_value(context, value_p,
1126 : 8 : static_cast<GValue *>(gboxed));
1127 : : }
1128 : :
1129 : : /* The only way to differentiate unions and structs is from
1130 : : * their g-i info as both GBoxed */
1131 : 165 : GjsAutoBaseInfo info = g_irepository_find_by_gtype(nullptr, gtype);
1132 [ - + ]: 165 : if (!info) {
1133 : 0 : gjs_throw(context,
1134 : : "No introspection information found for %s",
1135 : : g_type_name(gtype));
1136 : 0 : return false;
1137 : : }
1138 : :
1139 [ + - + + : 330 : if (info.type() == GI_INFO_TYPE_STRUCT &&
+ + ]
1140 : 165 : g_struct_info_is_foreign(info)) {
1141 : : GIArgument arg;
1142 : 1 : gjs_arg_set(&arg, gboxed);
1143 : 1 : return gjs_struct_foreign_convert_from_gi_argument(context, value_p,
1144 : 1 : info, &arg);
1145 : : }
1146 : :
1147 : 164 : GIInfoType type = info.type();
1148 [ + - + - ]: 164 : if (type == GI_INFO_TYPE_BOXED || type == GI_INFO_TYPE_STRUCT) {
1149 [ + + ]: 164 : if (no_copy)
1150 : 1 : obj = BoxedInstance::new_for_c_struct(context, info, gboxed,
1151 : : BoxedInstance::NoCopy());
1152 : : else
1153 : 163 : obj = BoxedInstance::new_for_c_struct(context, info, gboxed);
1154 [ # # ]: 0 : } else if (type == GI_INFO_TYPE_UNION) {
1155 : 0 : obj = UnionInstance::new_for_c_union(context, info, gboxed);
1156 : : } else {
1157 : 0 : gjs_throw(context, "Unexpected introspection type %d for %s",
1158 : 0 : info.type(), g_type_name(gtype));
1159 : 0 : return false;
1160 : : }
1161 : :
1162 : 164 : value_p.setObjectOrNull(obj);
1163 [ + + + + : 461 : } else if (g_type_is_a(gtype, G_TYPE_ENUM)) {
+ + + + ]
1164 : 9 : value_p.set(convert_int_to_enum(gtype, g_value_get_enum(gvalue)));
1165 [ + + - + : 287 : } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
+ + ]
1166 : : GParamSpec *gparam;
1167 : : JSObject *obj;
1168 : :
1169 : 179 : gparam = g_value_get_param(gvalue);
1170 : :
1171 : 179 : obj = gjs_param_from_g_param(context, gparam);
1172 : 179 : value_p.setObjectOrNull(obj);
1173 [ - + - - : 108 : } else if (is_introspected_signal && g_type_is_a(gtype, G_TYPE_POINTER)) {
- - - + ]
1174 [ # # ]: 0 : if (!arg_info) {
1175 : 0 : gjs_throw(context, "Unknown signal.");
1176 : 0 : return false;
1177 : : }
1178 : :
1179 : 0 : g_assert(((void)"Check gjs_value_from_array_and_length_values() before"
1180 : : " calling gjs_value_from_g_value_internal()",
1181 : : g_type_info_get_array_length(type_info) == -1));
1182 : :
1183 : : GIArgument arg;
1184 : 0 : gjs_arg_set(&arg, g_value_get_pointer(gvalue));
1185 : :
1186 : 0 : return gjs_value_from_gi_argument(context, value_p, type_info, &arg,
1187 : 0 : true);
1188 [ + + ]: 108 : } else if (gtype == G_TYPE_GTYPE) {
1189 : 3 : GType gvalue_gtype = g_value_get_gtype(gvalue);
1190 : :
1191 [ - + ]: 3 : if (gvalue_gtype == 0) {
1192 : 0 : value_p.setNull();
1193 : 0 : return true;
1194 : : }
1195 : :
1196 : : JS::RootedObject obj(
1197 : 3 : context, gjs_gtype_create_gtype_wrapper(context, gvalue_gtype));
1198 [ - + ]: 3 : if (!obj)
1199 : 0 : return false;
1200 : :
1201 : 3 : value_p.setObject(*obj);
1202 [ + - + - : 108 : } else if (g_type_is_a(gtype, G_TYPE_POINTER)) {
- + - + ]
1203 [ # # ]: 0 : if (g_value_get_pointer(gvalue) != nullptr) {
1204 : 0 : gjs_throw(context,
1205 : : "Can't convert non-null pointer to JS value");
1206 : 0 : return false;
1207 : : }
1208 [ + + ]: 105 : } else if (g_value_type_transformable(gtype, G_TYPE_DOUBLE)) {
1209 : 84 : GValue double_value = { 0, };
1210 : : double v;
1211 : 84 : g_value_init(&double_value, G_TYPE_DOUBLE);
1212 : 84 : g_value_transform(gvalue, &double_value);
1213 : 84 : v = g_value_get_double(&double_value);
1214 : 84 : value_p.setNumber(v);
1215 [ + + ]: 21 : } else if (g_value_type_transformable(gtype, G_TYPE_INT)) {
1216 : 9 : GValue int_value = { 0, };
1217 : : int v;
1218 : 9 : g_value_init(&int_value, G_TYPE_INT);
1219 : 9 : g_value_transform(gvalue, &int_value);
1220 : 9 : v = g_value_get_int(&int_value);
1221 : 9 : value_p.set(JS::NumberValue(v));
1222 [ + - ]: 12 : } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
1223 : : /* The gtype is none of the above, it should be a custom
1224 : : fundamental type. */
1225 : 12 : JS::RootedObject obj(context);
1226 [ - + ]: 12 : if (!FundamentalInstance::object_for_gvalue(context, gvalue, gtype,
1227 : : &obj))
1228 : 0 : return false;
1229 : :
1230 : 12 : value_p.setObjectOrNull(obj);
1231 [ + - ]: 12 : } else {
1232 : 0 : gjs_throw(context,
1233 : : "Don't know how to convert GType %s to JavaScript object",
1234 : : g_type_name(gtype));
1235 : 0 : return false;
1236 : : }
1237 : :
1238 : 768 : return true;
1239 : : }
1240 : :
1241 : : bool
1242 : 848 : gjs_value_from_g_value(JSContext *context,
1243 : : JS::MutableHandleValue value_p,
1244 : : const GValue *gvalue)
1245 : : {
1246 : 848 : return gjs_value_from_g_value_internal(context, value_p, gvalue, false);
1247 : : }
|