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 <stdint.h>
8 : :
9 : : #include <girepository.h>
10 : : #include <glib-object.h>
11 : :
12 : : #include <js/CallAndConstruct.h>
13 : : #include <js/CallArgs.h>
14 : : #include <js/Class.h>
15 : : #include <js/Exception.h>
16 : : #include <js/PropertyAndElement.h>
17 : : #include <js/PropertyDescriptor.h> // for JSPROP_ENUMERATE
18 : : #include <js/RootingAPI.h>
19 : : #include <js/SavedFrameAPI.h>
20 : : #include <js/Stack.h> // for BuildStackString, CaptureCurrentStack
21 : : #include <js/TypeDecls.h>
22 : : #include <js/Utility.h> // for UniqueChars
23 : : #include <js/Value.h>
24 : : #include <js/ValueArray.h>
25 : : #include <jsapi.h> // for InformalValueTypeName, JS_GetClassObject
26 : : #include <jspubtd.h> // for JSProtoKey, JSProto_Error, JSProt...
27 : :
28 : : #include "gi/arg-inl.h"
29 : : #include "gi/boxed.h"
30 : : #include "gi/enumeration.h"
31 : : #include "gi/gerror.h"
32 : : #include "gi/repo.h"
33 : : #include "gjs/atoms.h"
34 : : #include "gjs/context-private.h"
35 : : #include "gjs/error-types.h"
36 : : #include "gjs/jsapi-util.h"
37 : : #include "gjs/macros.h"
38 : : #include "gjs/mem-private.h"
39 : : #include "util/log.h"
40 : :
41 : 11 : ErrorPrototype::ErrorPrototype(GIEnumInfo* info, GType gtype)
42 : : : GIWrapperPrototype(info, gtype),
43 : 11 : m_domain(g_quark_from_string(g_enum_info_get_error_domain(info))) {
44 : 11 : GJS_INC_COUNTER(gerror_prototype);
45 : 11 : }
46 : :
47 : 11 : ErrorPrototype::~ErrorPrototype(void) { GJS_DEC_COUNTER(gerror_prototype); }
48 : :
49 : 37 : ErrorInstance::ErrorInstance(ErrorPrototype* prototype, JS::HandleObject obj)
50 : 37 : : GIWrapperInstance(prototype, obj) {
51 : 37 : GJS_INC_COUNTER(gerror_instance);
52 : 37 : }
53 : :
54 : 37 : ErrorInstance::~ErrorInstance(void) {
55 : 37 : GJS_DEC_COUNTER(gerror_instance);
56 : 37 : }
57 : :
58 : : /*
59 : : * ErrorBase::domain:
60 : : *
61 : : * Fetches ErrorPrototype::domain() for instances as well as prototypes.
62 : : */
63 : 25 : GQuark ErrorBase::domain(void) const { return get_prototype()->domain(); }
64 : :
65 : : // See GIWrapperBase::constructor().
66 : 9 : bool ErrorInstance::constructor_impl(JSContext* context,
67 : : JS::HandleObject object,
68 : : const JS::CallArgs& argv) {
69 [ + - - + : 9 : if (argv.length() != 1 || !argv[0].isObject()) {
- + ]
70 : 0 : gjs_throw(context, "Invalid parameters passed to GError constructor, expected one object");
71 : 0 : return false;
72 : : }
73 : :
74 : 9 : JS::RootedObject params_obj(context, &argv[0].toObject());
75 : 9 : JS::UniqueChars message;
76 : 9 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
77 [ - + ]: 9 : if (!gjs_object_require_property(context, params_obj, "GError constructor",
78 : : atoms.message(), &message))
79 : 0 : return false;
80 : :
81 : : int32_t code;
82 [ - + ]: 9 : if (!gjs_object_require_property(context, params_obj, "GError constructor",
83 : : atoms.code(), &code))
84 : 0 : return false;
85 : :
86 : 9 : m_ptr = g_error_new_literal(domain(), code, message.get());
87 : :
88 : : /* We assume this error will be thrown in the same line as the constructor */
89 : 9 : return gjs_define_error_properties(context, object);
90 : 9 : }
91 : :
92 : : /*
93 : : * ErrorBase::get_domain:
94 : : *
95 : : * JSNative property getter for `domain`. This property works on prototypes as
96 : : * well as instances.
97 : : */
98 : 5 : bool ErrorBase::get_domain(JSContext* cx, unsigned argc, JS::Value* vp) {
99 [ - + - + ]: 5 : GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ErrorBase, priv);
100 : 5 : args.rval().setInt32(priv->domain());
101 : 5 : return true;
102 : 5 : }
103 : :
104 : : // JSNative property getter for `message`.
105 : 3 : bool ErrorBase::get_message(JSContext* cx, unsigned argc, JS::Value* vp) {
106 [ - + - + ]: 3 : GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ErrorBase, priv);
107 [ - + ]: 3 : if (!priv->check_is_instance(cx, "get a field"))
108 : 0 : return false;
109 : :
110 : 3 : return gjs_string_from_utf8(cx, priv->to_instance()->message(),
111 : 3 : args.rval());
112 : 3 : }
113 : :
114 : : // JSNative property getter for `code`.
115 : 6 : bool ErrorBase::get_code(JSContext* cx, unsigned argc, JS::Value* vp) {
116 [ - + - + ]: 6 : GJS_CHECK_WRAPPER_PRIV(cx, argc, vp, args, obj, ErrorBase, priv);
117 [ - + ]: 6 : if (!priv->check_is_instance(cx, "get a field"))
118 : 0 : return false;
119 : :
120 : 6 : args.rval().setInt32(priv->to_instance()->code());
121 : 6 : return true;
122 : 6 : }
123 : :
124 : : // JSNative implementation of `toString()`.
125 : 10 : bool ErrorBase::to_string(JSContext* context, unsigned argc, JS::Value* vp) {
126 [ - + ]: 10 : GJS_GET_THIS(context, argc, vp, rec, self);
127 : :
128 : 10 : GjsAutoChar descr;
129 : :
130 : : // An error created via `new GLib.Error` will have a Boxed* private pointer,
131 : : // not an Error*, so we can't call regular to_string() on it.
132 [ + + ]: 10 : if (BoxedBase::typecheck(context, self, nullptr, G_TYPE_ERROR,
133 : : GjsTypecheckNoThrow())) {
134 : 2 : auto* gerror = BoxedBase::to_c_ptr<GError>(context, self);
135 [ - + ]: 2 : if (!gerror)
136 : 0 : return false;
137 : : descr =
138 : : g_strdup_printf("GLib.Error %s: %s",
139 : 2 : g_quark_to_string(gerror->domain), gerror->message);
140 : :
141 : 2 : return gjs_string_from_utf8(context, descr, rec.rval());
142 : : }
143 : :
144 : : ErrorBase* priv;
145 [ - + ]: 8 : if (!for_js_typecheck(context, self, &priv, &rec))
146 : 0 : return false;
147 : :
148 : : /* We follow the same pattern as standard JS errors, at the expense of
149 : : hiding some useful information */
150 : :
151 [ - + ]: 8 : if (priv->is_prototype()) {
152 : 0 : descr = g_strdup_printf("%s.%s", priv->ns(), priv->name());
153 : : } else {
154 : : descr = g_strdup_printf("%s.%s: %s", priv->ns(), priv->name(),
155 : 8 : priv->to_instance()->message());
156 : : }
157 : :
158 : 8 : return gjs_string_from_utf8(context, descr, rec.rval());
159 : 10 : }
160 : :
161 : : // JSNative implementation of `valueOf()`.
162 : 11 : bool ErrorBase::value_of(JSContext* context, unsigned argc, JS::Value* vp) {
163 [ - + ]: 11 : GJS_GET_THIS(context, argc, vp, rec, self);
164 : 11 : JS::RootedObject prototype(context);
165 : 11 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
166 : :
167 [ - + ]: 11 : if (!gjs_object_require_property(context, self, "constructor",
168 : : atoms.prototype(), &prototype)) {
169 : : /* This error message will be more informative */
170 : 0 : JS_ClearPendingException(context);
171 : 0 : gjs_throw(context, "GLib.Error.valueOf() called on something that is not"
172 : : " a constructor");
173 : 0 : return false;
174 : : }
175 : :
176 : : ErrorBase* priv;
177 [ - + ]: 11 : if (!for_js_typecheck(context, prototype, &priv, &rec))
178 : 0 : return false;
179 : :
180 : 11 : rec.rval().setInt32(priv->domain());
181 : 11 : return true;
182 : 11 : }
183 : :
184 : : // clang-format off
185 : : const struct JSClassOps ErrorBase::class_ops = {
186 : : nullptr, // addProperty
187 : : nullptr, // deleteProperty
188 : : nullptr, // enumerate
189 : : nullptr, // newEnumerate
190 : : nullptr, // resolve
191 : : nullptr, // mayResolve
192 : : &ErrorBase::finalize,
193 : : };
194 : :
195 : : const struct JSClass ErrorBase::klass = {
196 : : "GLib_Error",
197 : : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
198 : : &ErrorBase::class_ops
199 : : };
200 : :
201 : : /* We need to shadow all fields of GError, to prevent calling the getter from GBoxed
202 : : (which would trash memory accessing the instance private data) */
203 : : JSPropertySpec ErrorBase::proto_properties[] = {
204 : : JS_PSG("domain", &ErrorBase::get_domain, GJS_MODULE_PROP_FLAGS),
205 : : JS_PSG("code", &ErrorBase::get_code, GJS_MODULE_PROP_FLAGS),
206 : : JS_PSG("message", &ErrorBase::get_message, GJS_MODULE_PROP_FLAGS),
207 : : JS_PS_END
208 : : };
209 : :
210 : : JSFunctionSpec ErrorBase::static_methods[] = {
211 : : JS_FN("valueOf", &ErrorBase::value_of, 0, GJS_MODULE_PROP_FLAGS),
212 : : JS_FS_END
213 : : };
214 : : // clang-format on
215 : :
216 : : // Overrides GIWrapperPrototype::get_parent_proto().
217 : 11 : bool ErrorPrototype::get_parent_proto(JSContext* cx,
218 : : JS::MutableHandleObject proto) const {
219 : 11 : g_irepository_require(nullptr, "GLib", "2.0", GIRepositoryLoadFlags(0),
220 : : nullptr);
221 : : GjsAutoStructInfo glib_error_info =
222 : 11 : g_irepository_find_by_name(nullptr, "GLib", "Error");
223 : 11 : proto.set(gjs_lookup_generic_prototype(cx, glib_error_info));
224 : 11 : return !!proto;
225 : 11 : }
226 : :
227 : 11 : bool ErrorPrototype::define_class(JSContext* context,
228 : : JS::HandleObject in_object,
229 : : GIEnumInfo* info) {
230 : 11 : JS::RootedObject prototype(context), constructor(context);
231 [ - + ]: 11 : if (!ErrorPrototype::create_class(context, in_object, info, G_TYPE_ERROR,
232 : : &constructor, &prototype))
233 : 0 : return false;
234 : :
235 : : // Define a toString() on the prototype, as it does not exist on the
236 : : // prototype of GLib.Error; and create_class() will not define it since we
237 : : // supply a parent in get_parent_proto().
238 : 11 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
239 : 11 : return JS_DefineFunctionById(context, prototype, atoms.to_string(),
240 : : &ErrorBase::to_string, 0,
241 [ + - + - ]: 22 : GJS_MODULE_PROP_FLAGS) &&
242 : 22 : gjs_define_enum_values(context, constructor, info);
243 : 11 : }
244 : :
245 : 37 : [[nodiscard]] static GIEnumInfo* find_error_domain_info(GQuark domain) {
246 : : GIEnumInfo *info;
247 : :
248 : : /* first an attempt without loading extra libraries */
249 : 37 : info = g_irepository_find_by_error_domain(nullptr, domain);
250 [ + + ]: 37 : if (info)
251 : 28 : return info;
252 : :
253 : : /* load standard stuff */
254 : 9 : g_irepository_require(nullptr, "GLib", "2.0", GIRepositoryLoadFlags(0),
255 : : nullptr);
256 : 9 : g_irepository_require(nullptr, "GObject", "2.0", GIRepositoryLoadFlags(0),
257 : : nullptr);
258 : 9 : g_irepository_require(nullptr, "Gio", "2.0", GIRepositoryLoadFlags(0),
259 : : nullptr);
260 : 9 : info = g_irepository_find_by_error_domain(nullptr, domain);
261 [ - + ]: 9 : if (info)
262 : 0 : return info;
263 : :
264 : : /* last attempt: load GIRepository (for invoke errors, rarely
265 : : needed) */
266 : 9 : g_irepository_require(nullptr, "GIRepository", "1.0",
267 : : GIRepositoryLoadFlags(0), nullptr);
268 : 9 : info = g_irepository_find_by_error_domain(nullptr, domain);
269 : :
270 : 9 : return info;
271 : : }
272 : :
273 : : /* define properties that JS Error() expose, such as
274 : : fileName, lineNumber and stack
275 : : */
276 : : GJS_JSAPI_RETURN_CONVENTION
277 : 59 : bool gjs_define_error_properties(JSContext* cx, JS::HandleObject obj) {
278 : 59 : JS::RootedObject frame(cx);
279 : 59 : JS::RootedString stack(cx);
280 : 59 : JS::RootedString source(cx);
281 : : uint32_t line, column;
282 : :
283 [ + - - + ]: 177 : if (!JS::CaptureCurrentStack(cx, &frame) ||
284 [ - + + - ]: 118 : !JS::BuildStackString(cx, nullptr, frame, &stack))
285 : 0 : return false;
286 : :
287 : 59 : auto ok = JS::SavedFrameResult::Ok;
288 [ + - ]: 118 : if (JS::GetSavedFrameSource(cx, nullptr, frame, &source) != ok ||
289 [ + - - + ]: 118 : JS::GetSavedFrameLine(cx, nullptr, frame, &line) != ok ||
290 [ - + ]: 118 : JS::GetSavedFrameColumn(cx, nullptr, frame, &column) != ok) {
291 : 0 : gjs_throw(cx, "Error getting saved frame information");
292 : 0 : return false;
293 : : }
294 : :
295 : 59 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
296 : 59 : return JS_DefinePropertyById(cx, obj, atoms.stack(), stack,
297 [ + - ]: 59 : JSPROP_ENUMERATE) &&
298 : 59 : JS_DefinePropertyById(cx, obj, atoms.file_name(), source,
299 [ + - ]: 59 : JSPROP_ENUMERATE) &&
300 : 59 : JS_DefinePropertyById(cx, obj, atoms.line_number(), line,
301 [ + - + - ]: 118 : JSPROP_ENUMERATE) &&
302 : 59 : JS_DefinePropertyById(cx, obj, atoms.column_number(), column,
303 : 59 : JSPROP_ENUMERATE);
304 : 59 : }
305 : :
306 : 13 : [[nodiscard]] static JSProtoKey proto_key_from_error_enum(int val) {
307 [ - - - + : 13 : switch (val) {
- + - + ]
308 : 0 : case GJS_JS_ERROR_EVAL_ERROR:
309 : 0 : return JSProto_EvalError;
310 : 0 : case GJS_JS_ERROR_INTERNAL_ERROR:
311 : 0 : return JSProto_InternalError;
312 : 0 : case GJS_JS_ERROR_RANGE_ERROR:
313 : 0 : return JSProto_RangeError;
314 : 1 : case GJS_JS_ERROR_REFERENCE_ERROR:
315 : 1 : return JSProto_ReferenceError;
316 : 0 : case GJS_JS_ERROR_SYNTAX_ERROR:
317 : 0 : return JSProto_SyntaxError;
318 : 2 : case GJS_JS_ERROR_TYPE_ERROR:
319 : 2 : return JSProto_TypeError;
320 : 0 : case GJS_JS_ERROR_URI_ERROR:
321 : 0 : return JSProto_URIError;
322 : 10 : case GJS_JS_ERROR_ERROR:
323 : : default:
324 : 10 : return JSProto_Error;
325 : : }
326 : : }
327 : :
328 : : GJS_JSAPI_RETURN_CONVENTION
329 : : static JSObject *
330 : 13 : gjs_error_from_js_gerror(JSContext *cx,
331 : : GError *gerror)
332 : : {
333 : 13 : JS::RootedValueArray<1> error_args(cx);
334 [ - + ]: 13 : if (!gjs_string_from_utf8(cx, gerror->message, error_args[0]))
335 : 0 : return nullptr;
336 : :
337 : 13 : JSProtoKey error_kind = proto_key_from_error_enum(gerror->code);
338 : 13 : JS::RootedObject error_constructor(cx);
339 [ - + ]: 13 : if (!JS_GetClassObject(cx, error_kind, &error_constructor))
340 : 0 : return nullptr;
341 : :
342 : : JS::RootedValue v_error_constructor(cx,
343 : 13 : JS::ObjectValue(*error_constructor));
344 : 13 : JS::RootedObject error(cx);
345 [ - + ]: 13 : if (!JS::Construct(cx, v_error_constructor, error_args, &error))
346 : 0 : return nullptr;
347 : :
348 : 13 : return error;
349 : 13 : }
350 : :
351 : 50 : JSObject* ErrorInstance::object_for_c_ptr(JSContext* context, GError* gerror) {
352 : : GIEnumInfo *info;
353 : :
354 [ - + ]: 50 : if (!gerror)
355 : 0 : return nullptr;
356 : :
357 [ + + ]: 50 : if (gerror->domain == GJS_JS_ERROR)
358 : 13 : return gjs_error_from_js_gerror(context, gerror);
359 : :
360 : 37 : info = find_error_domain_info(gerror->domain);
361 : :
362 [ + + ]: 37 : if (!info) {
363 : : /* We don't have error domain metadata */
364 : : /* Marshal the error as a plain GError */
365 : : GjsAutoBaseInfo glib_boxed =
366 : 9 : g_irepository_find_by_name(nullptr, "GLib", "Error");
367 : 9 : return BoxedInstance::new_for_c_struct(context, glib_boxed, gerror);
368 : 9 : }
369 : :
370 : : gjs_debug_marshal(GJS_DEBUG_GBOXED,
371 : : "Wrapping struct %s with JSObject",
372 : : g_base_info_get_name((GIBaseInfo *)info));
373 : :
374 : : JS::RootedObject obj(context,
375 : 28 : gjs_new_object_with_generic_prototype(context, info));
376 [ - + ]: 28 : if (!obj)
377 : 0 : return nullptr;
378 : :
379 : 28 : ErrorInstance* priv = ErrorInstance::new_for_js_object(context, obj);
380 : 28 : priv->copy_gerror(gerror);
381 : :
382 : 28 : return obj;
383 : 28 : }
384 : :
385 : 11 : GError* ErrorBase::to_c_ptr(JSContext* cx, JS::HandleObject obj) {
386 : : /* If this is a plain GBoxed (i.e. a GError without metadata),
387 : : delegate marshalling.
388 : : */
389 [ + + ]: 11 : if (BoxedBase::typecheck(cx, obj, nullptr, G_TYPE_ERROR,
390 : : GjsTypecheckNoThrow()))
391 : 2 : return BoxedBase::to_c_ptr<GError>(cx, obj);
392 : :
393 : 9 : return GIWrapperBase::to_c_ptr<GError>(cx, obj);
394 : : }
395 : :
396 : 8 : bool ErrorBase::transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
397 : : GIArgument* arg,
398 : : GIDirection transfer_direction,
399 : : GITransfer transfer_ownership) {
400 : 8 : g_assert(transfer_direction != GI_DIRECTION_INOUT &&
401 : : "transfer_to_gi_argument() must choose between in or out");
402 : :
403 [ - + ]: 8 : if (!ErrorBase::typecheck(cx, obj)) {
404 : 0 : gjs_arg_unset<void*>(arg);
405 : 0 : return false;
406 : : }
407 : :
408 : 8 : gjs_arg_set(arg, ErrorBase::to_c_ptr(cx, obj));
409 [ - + ]: 8 : if (!gjs_arg_get<void*>(arg))
410 : 0 : return false;
411 : :
412 [ + - + - ]: 8 : if ((transfer_direction == GI_DIRECTION_IN &&
413 [ - + ]: 8 : transfer_ownership != GI_TRANSFER_NOTHING) ||
414 [ # # ]: 0 : (transfer_direction == GI_DIRECTION_OUT &&
415 : : transfer_ownership == GI_TRANSFER_EVERYTHING)) {
416 : 0 : gjs_arg_set(arg, ErrorInstance::copy_ptr(cx, G_TYPE_ERROR,
417 : : gjs_arg_get<void*>(arg)));
418 [ # # ]: 0 : if (!gjs_arg_get<void*>(arg))
419 : 0 : return false;
420 : : }
421 : :
422 : 8 : return true;
423 : : }
424 : :
425 : : // Overrides GIWrapperBase::typecheck()
426 : 8 : bool ErrorBase::typecheck(JSContext* cx, JS::HandleObject obj) {
427 [ + + ]: 8 : if (BoxedBase::typecheck(cx, obj, nullptr, G_TYPE_ERROR,
428 : : GjsTypecheckNoThrow()))
429 : 2 : return true;
430 : 6 : return GIWrapperBase::typecheck(cx, obj, nullptr, G_TYPE_ERROR);
431 : : }
432 : :
433 : 8 : bool ErrorBase::typecheck(JSContext* cx, JS::HandleObject obj,
434 : : GjsTypecheckNoThrow no_throw) {
435 [ - + ]: 8 : if (BoxedBase::typecheck(cx, obj, nullptr, G_TYPE_ERROR, no_throw))
436 : 0 : return true;
437 : 8 : return GIWrapperBase::typecheck(cx, obj, nullptr, G_TYPE_ERROR, no_throw);
438 : : }
439 : :
440 : : GJS_JSAPI_RETURN_CONVENTION
441 : 8 : static GError* gerror_from_error_impl(JSContext* cx, JS::HandleObject obj) {
442 [ + + ]: 8 : if (ErrorBase::typecheck(cx, obj, GjsTypecheckNoThrow())) {
443 : : /* This is already a GError, just copy it */
444 : 2 : GError* inner = ErrorBase::to_c_ptr(cx, obj);
445 [ - + ]: 2 : if (!inner)
446 : 0 : return nullptr;
447 : 2 : return g_error_copy(inner);
448 : : }
449 : :
450 : : /* Try to make something useful from the error
451 : : name and message (in case this is a JS error) */
452 : 6 : const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
453 : 6 : JS::RootedValue v_name(cx);
454 [ - + ]: 6 : if (!JS_GetPropertyById(cx, obj, atoms.name(), &v_name))
455 : 0 : return nullptr;
456 : :
457 : 6 : JS::RootedValue v_message(cx);
458 [ - + ]: 6 : if (!JS_GetPropertyById(cx, obj, atoms.message(), &v_message))
459 : 0 : return nullptr;
460 : :
461 [ + + + + : 6 : if (!v_name.isString() || !v_message.isString()) {
+ + ]
462 : 3 : return g_error_new_literal(
463 : : GJS_JS_ERROR, GJS_JS_ERROR_ERROR,
464 : 3 : "Object thrown with unexpected name or message property");
465 : : }
466 : :
467 : 3 : JS::UniqueChars name = gjs_string_to_utf8(cx, v_name);
468 [ - + ]: 3 : if (!name)
469 : 0 : return nullptr;
470 : :
471 : 3 : JS::UniqueChars message = gjs_string_to_utf8(cx, v_message);
472 [ - + ]: 3 : if (!message)
473 : 0 : return nullptr;
474 : :
475 : 3 : GjsAutoTypeClass<GEnumClass> klass(GJS_TYPE_JS_ERROR);
476 : 3 : const GEnumValue *value = g_enum_get_value_by_name(klass, name.get());
477 : : int code;
478 [ + - ]: 3 : if (value)
479 : 3 : code = value->value;
480 : : else
481 : 0 : code = GJS_JS_ERROR_ERROR;
482 : :
483 : 3 : return g_error_new_literal(GJS_JS_ERROR, code, message.get());
484 : 6 : }
485 : :
486 : : /*
487 : : * gjs_gerror_make_from_thrown_value:
488 : : *
489 : : * Attempts to convert a JavaScript thrown value (pending on @cx) into a
490 : : * #GError. This function is infallible and will always return a #GError with
491 : : * some message, even if the exception value couldn't be converted.
492 : : *
493 : : * Clears the pending exception on @cx.
494 : : *
495 : : * Returns: (transfer full): a new #GError
496 : : */
497 : 15 : GError* gjs_gerror_make_from_thrown_value(JSContext* cx) {
498 : 15 : g_assert(JS_IsExceptionPending(cx) &&
499 : : "Should be called when an exception is pending");
500 : :
501 : 15 : JS::RootedValue exc(cx);
502 : 15 : JS_GetPendingException(cx, &exc);
503 : 15 : JS_ClearPendingException(cx); // don't log
504 : :
505 [ + + ]: 15 : if (!exc.isObject()) {
506 : 7 : return g_error_new(GJS_JS_ERROR, GJS_JS_ERROR_ERROR,
507 : : "Non-exception %s value %s thrown",
508 : : JS::InformalValueTypeName(exc),
509 : 14 : gjs_debug_value(exc).c_str());
510 : : }
511 : :
512 : 8 : JS::RootedObject obj(cx, &exc.toObject());
513 : 8 : GError* retval = gerror_from_error_impl(cx, obj);
514 [ + - ]: 8 : if (retval)
515 : 8 : return retval;
516 : :
517 : : // Make a GError with an InternalError even if it wasn't possible to convert
518 : : // the exception into one
519 : 0 : gjs_log_exception(cx); // log the inner exception
520 : 0 : return g_error_new_literal(GJS_JS_ERROR, GJS_JS_ERROR_INTERNAL_ERROR,
521 : 0 : "Failed to convert JS thrown value into GError");
522 : 15 : }
523 : :
524 : : /*
525 : : * gjs_throw_gerror:
526 : : *
527 : : * Converts a GError into a JavaScript exception, and frees the GError.
528 : : * Differently from gjs_throw(), it will overwrite an existing exception, as it
529 : : * is used to report errors from C functions.
530 : : *
531 : : * Returns: false, for convenience in returning from the calling function.
532 : : */
533 : 38 : bool gjs_throw_gerror(JSContext* cx, GjsAutoError const& error) {
534 : : // return false even if the GError is null, as presumably something failed
535 : : // in the calling code, and the caller expects to throw.
536 : 38 : g_return_val_if_fail(error, false);
537 : :
538 : 38 : JS::RootedObject err_obj(cx, ErrorInstance::object_for_c_ptr(cx, error));
539 [ + - - + : 38 : if (!err_obj || !gjs_define_error_properties(cx, err_obj))
- + ]
540 : 0 : return false;
541 : :
542 : 38 : JS::RootedValue err(cx, JS::ObjectValue(*err_obj));
543 : 38 : JS_SetPendingException(cx, err);
544 : :
545 : 38 : return false;
546 : 38 : }
|