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