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