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