| 93 | } |
| 94 | |
| 95 | [[gnu::format(printf, 4, 0)]] |
| 96 | static void gjs_throw_valist(JSContext* cx, JSExnType error_kind, |
| 97 | const char* error_name, const char* format, |
| 98 | va_list args) { |
| 99 | Gjs::AutoChar s{g_strdup_vprintf(format, args)}; |
| 100 | auto fallback = mozilla::MakeScopeExit([cx, &s]() { |
| 101 | // try just reporting it to error handler? should not |
| 102 | // happen though pretty much |
| 103 | JS_ReportErrorUTF8(cx, "Failed to throw exception '%s'", s.get()); |
| 104 | }); |
| 105 | |
| 106 | JS::ConstUTF8CharsZ chars{s.get(), strlen(s.get())}; |
| 107 | JS::RootedString message{cx, JS_NewStringCopyUTF8Z(cx, chars)}; |
| 108 | if (!message) |
| 109 | return; |
| 110 | |
| 111 | JS::RootedObject saved_frame{cx}; |
| 112 | if (!JS::CaptureCurrentStack(cx, &saved_frame)) |
| 113 | return; |
| 114 | |
| 115 | JS::RootedString source_string{cx}; |
| 116 | JS::GetSavedFrameSource(cx, /* principals = */ nullptr, saved_frame, |
| 117 | &source_string); |
| 118 | uint32_t line_num; |
| 119 | JS::GetSavedFrameLine(cx, nullptr, saved_frame, &line_num); |
| 120 | JS::TaggedColumnNumberOneOrigin tagged_column; |
| 121 | JS::GetSavedFrameColumn(cx, nullptr, saved_frame, &tagged_column); |
| 122 | JS::ColumnNumberOneOrigin column_num{tagged_column.toLimitedColumnNumber()}; |
| 123 | // asserts that this isn't a WASM frame |
| 124 | |
| 125 | JS::RootedValue v_exc{cx}; |
| 126 | if (!JS::CreateError(cx, error_kind, saved_frame, source_string, line_num, |
| 127 | column_num, /* report = */ nullptr, message, |
| 128 | /* cause = */ JS::NothingHandleValue, &v_exc)) |
| 129 | return; |
| 130 | |
| 131 | if (error_name) { |
| 132 | const GjsAtoms& atoms = GjsContextPrivate::atoms(cx); |
| 133 | JS::RootedValue v_name{cx}; |
| 134 | JS::RootedObject exc{cx, &v_exc.toObject()}; |
| 135 | if (!gjs_string_from_utf8(cx, error_name, &v_name) || |
| 136 | !JS_SetPropertyById(cx, exc, atoms.name(), v_name)) |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | if (JS_IsExceptionPending(cx)) { |
| 141 | // Often it's unclear whether a given jsapi.h function will throw an |
| 142 | // exception, so we will throw ourselves "just in case"; in those cases, |
| 143 | // we append the new exception as the cause of the original exception. |
| 144 | // The second exception may add more info. |
| 145 | JS::RootedValue pending(cx); |
| 146 | JS_GetPendingException(cx, &pending); |
| 147 | JS::AutoSaveExceptionState saved_exc{cx}; |
| 148 | bool appended; |
| 149 | if (!append_new_cause(cx, pending, v_exc, &appended)) |
| 150 | saved_exc.restore(); |
| 151 | if (!appended) |
| 152 | gjs_debug(GJS_DEBUG_CONTEXT, "Ignoring second exception: '%s'", |
no test coverage detected