1use std::borrow::Cow;
8use std::fmt::{Display, Formatter};
9use std::{error, fmt, ptr};
10
11use mozjs::error::{throw_internal_error, throw_range_error, throw_type_error};
12use mozjs::jsapi::{CreateError, JS_ReportErrorUTF8, JSExnType, JSObject, JSProtoKey, UndefinedHandleValue};
13
14use crate::conversions::ToValue;
15use crate::exception::ThrowException;
16use crate::stack::Location;
17use crate::{Context, ErrorReport, Exception, Object, Stack, Value};
18
19#[derive(Clone, Debug, Eq, PartialEq)]
21pub enum ErrorKind {
22 Normal,
23 Internal,
24 Aggregate,
25 Eval,
26 Range,
27 Reference,
28 Syntax,
29 Type,
30 WasmCompile,
31 WasmLink,
32 WasmRuntime,
33 None,
34}
35
36impl ErrorKind {
37 pub fn from_proto_key(key: JSProtoKey) -> ErrorKind {
40 use ErrorKind as EK;
41 use JSProtoKey::{
42 JSProto_AggregateError, JSProto_CompileError, JSProto_Error, JSProto_EvalError, JSProto_InternalError,
43 JSProto_LinkError, JSProto_RangeError, JSProto_ReferenceError, JSProto_RuntimeError, JSProto_SyntaxError,
44 JSProto_TypeError,
45 };
46 match key {
47 JSProto_Error => EK::Normal,
48 JSProto_InternalError => EK::Internal,
49 JSProto_AggregateError => EK::Aggregate,
50 JSProto_EvalError => EK::Eval,
51 JSProto_RangeError => EK::Range,
52 JSProto_ReferenceError => EK::Reference,
53 JSProto_SyntaxError => EK::Syntax,
54 JSProto_TypeError => EK::Type,
55 JSProto_CompileError => EK::WasmCompile,
56 JSProto_LinkError => EK::WasmLink,
57 JSProto_RuntimeError => EK::WasmRuntime,
58 _ => EK::None,
59 }
60 }
61
62 pub fn to_exception_type(&self) -> JSExnType {
66 use {ErrorKind as EK, JSExnType as JSET};
67 match self {
68 EK::Normal | EK::None => JSET::JSEXN_ERR,
69 EK::Internal => JSET::JSEXN_INTERNALERR,
70 EK::Aggregate => JSET::JSEXN_AGGREGATEERR,
71 EK::Eval => JSET::JSEXN_EVALERR,
72 EK::Range => JSET::JSEXN_RANGEERR,
73 EK::Reference => JSET::JSEXN_REFERENCEERR,
74 EK::Syntax => JSET::JSEXN_SYNTAXERR,
75 EK::Type => JSET::JSEXN_TYPEERR,
76 EK::WasmCompile => JSET::JSEXN_WASMCOMPILEERROR,
77 EK::WasmLink => JSET::JSEXN_WASMLINKERROR,
78 EK::WasmRuntime => JSET::JSEXN_WASMRUNTIMEERROR,
79 }
80 }
81}
82
83impl Display for ErrorKind {
84 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
85 use ErrorKind as EK;
86 let str = match self {
87 EK::Normal => "Error",
88 EK::Internal => "InternalError",
89 EK::Aggregate => "AggregateError",
90 EK::Eval => "EvalError",
91 EK::Range => "RangeError",
92 EK::Reference => "ReferenceError",
93 EK::Syntax => "SyntaxError",
94 EK::Type => "TypeError",
95 EK::WasmCompile => "CompileError",
96 EK::WasmLink => "LinkError",
97 EK::WasmRuntime => "RuntimeError",
98 EK::None => "Not an Error",
99 };
100 f.write_str(str)
101 }
102}
103
104#[derive(Clone, Debug)]
109pub struct Error {
110 pub kind: ErrorKind,
111 pub message: Cow<'static, str>,
112 pub location: Option<Location>,
113 pub object: Option<*mut JSObject>,
114}
115
116impl Error {
117 pub fn new<M: Into<Cow<'static, str>>, K: Into<Option<ErrorKind>>>(message: M, kind: K) -> Error {
118 Error {
119 kind: kind.into().unwrap_or(ErrorKind::Normal),
120 message: message.into(),
121 location: None,
122 object: None,
123 }
124 }
125
126 pub fn none() -> Error {
127 Error {
128 kind: ErrorKind::None,
129 message: Cow::Borrowed(""),
130 location: None,
131 object: None,
132 }
133 }
134
135 pub fn to_object<'cx>(&self, cx: &'cx Context) -> Option<Object<'cx>> {
136 if let Some(object) = self.object {
137 return Some(cx.root(object).into());
138 }
139 if self.kind != ErrorKind::None {
140 unsafe {
141 let exception_type = self.kind.to_exception_type();
142
143 let stack = Stack::from_capture(cx).unwrap();
144 let (file, lineno, column) = stack
145 .records
146 .first()
147 .map(|record| &record.location)
148 .map(|location| (&*location.file, location.lineno, location.column))
149 .unwrap_or_default();
150
151 let stack = Object::from(cx.root(stack.object.unwrap()));
152
153 let file = file.as_value(cx);
154
155 let file_name = cx.root(file.handle().to_string());
156
157 let message = (!self.message.is_empty()).then(|| {
158 let value = self.message.as_value(cx);
159 crate::String::from(cx.root(value.handle().to_string()))
160 });
161 let message = message.unwrap_or_else(|| crate::String::from(cx.root(ptr::null_mut())));
162
163 let mut error = Value::undefined(cx);
164
165 if CreateError(
166 cx.as_ptr(),
167 exception_type,
168 stack.handle().into(),
169 file_name.handle().into(),
170 lineno,
171 column,
172 ptr::null_mut(),
173 message.handle().into(),
174 UndefinedHandleValue,
175 error.handle_mut().into(),
176 ) {
177 return Some(error.to_object(cx));
178 }
179 }
180 }
181 None
182 }
183
184 pub fn format(&self) -> String {
185 let Error { kind, message, location, .. } = self;
186 let message = if message.is_empty() {
187 String::new()
188 } else {
189 format!(" - {message}")
190 };
191
192 if let Some(location) = location {
193 let Location { file, lineno, column } = location;
194 if !file.is_empty() {
195 return if *lineno == 0 {
196 format!("{kind} at {file}{message}")
197 } else if *column == 0 {
198 format!("{kind} at {file}:{lineno}{message}")
199 } else {
200 format!("{kind} at {file}:{lineno}:{column}{message}")
201 };
202 }
203 }
204 format!("{kind}{message}")
205 }
206}
207
208impl Display for Error {
209 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
210 f.write_str(&self.message)
211 }
212}
213
214impl From<Error> for fmt::Error {
215 fn from(_: Error) -> fmt::Error {
216 fmt::Error
217 }
218}
219
220impl From<Error> for ErrorReport {
221 fn from(error: Error) -> ErrorReport {
222 ErrorReport::from_exception(Exception::Error(error), None)
223 }
224}
225
226impl<E: error::Error> From<E> for Error {
227 fn from(error: E) -> Error {
228 Error::new(error.to_string(), None)
229 }
230}
231
232impl ThrowException for Error {
233 fn throw(&self, cx: &Context) {
234 unsafe {
235 use ErrorKind as EK;
236 match self.kind {
237 EK::Normal => JS_ReportErrorUTF8(cx.as_ptr(), format!("{}\0", self.message).as_ptr().cast()),
238 EK::Internal => throw_internal_error(cx.as_ptr(), &self.message),
239 EK::Range => throw_range_error(cx.as_ptr(), &self.message),
240 EK::Type => throw_type_error(cx.as_ptr(), &self.message),
241 EK::None => (),
242 _ => unimplemented!("Throwing Exception for this is not implemented"),
243 }
244 }
245 }
246}
247
248impl<'cx> ToValue<'cx> for Error {
249 fn to_value(&self, cx: &'cx Context, value: &mut Value) {
250 self.to_object(cx).to_value(cx, value);
251 }
252}