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