Skip to main content

v8/
exception.rs

1#![allow(non_snake_case)]
2
3use std::convert::TryInto;
4
5use crate::Context;
6use crate::Local;
7use crate::Message;
8use crate::Object;
9use crate::StackFrame;
10use crate::StackTrace;
11use crate::String;
12use crate::Value;
13use crate::isolate::RealIsolate;
14use crate::scope::PinScope;
15use crate::support::MaybeBool;
16use crate::support::int;
17
18unsafe extern "C" {
19  fn v8__Message__Get(this: *const Message) -> *const String;
20  fn v8__Message__GetSourceLine(
21    this: *const Message,
22    context: *const Context,
23  ) -> *const String;
24  fn v8__Message__GetScriptResourceName(this: *const Message) -> *const Value;
25  fn v8__Message__GetLineNumber(
26    this: *const Message,
27    context: *const Context,
28  ) -> int;
29  fn v8__Message__GetStartPosition(this: *const Message) -> int;
30  fn v8__Message__GetEndPosition(this: *const Message) -> int;
31  fn v8__Message__GetWasmFunctionIndex(this: *const Message) -> int;
32  fn v8__Message__ErrorLevel(this: *const Message) -> int;
33  fn v8__Message__GetStartColumn(this: *const Message) -> int;
34  fn v8__Message__GetEndColumn(this: *const Message) -> int;
35  fn v8__Message__IsSharedCrossOrigin(this: *const Message) -> bool;
36  fn v8__Message__IsOpaque(this: *const Message) -> bool;
37  fn v8__Message__GetStackTrace(this: *const Message) -> *const StackTrace;
38
39  fn v8__StackTrace__CurrentStackTrace(
40    isolate: *mut RealIsolate,
41    frame_limit: int,
42  ) -> *const StackTrace;
43  fn v8__StackTrace__CurrentScriptNameOrSourceURL(
44    isolate: *mut RealIsolate,
45  ) -> *const String;
46  fn v8__StackTrace__GetFrameCount(this: *const StackTrace) -> int;
47  fn v8__StackTrace__GetFrame(
48    this: *const StackTrace,
49    isolate: *mut RealIsolate,
50    index: u32,
51  ) -> *const StackFrame;
52
53  fn v8__StackFrame__GetLineNumber(this: *const StackFrame) -> int;
54  fn v8__StackFrame__GetColumn(this: *const StackFrame) -> int;
55  fn v8__StackFrame__GetScriptId(this: *const StackFrame) -> int;
56  fn v8__StackFrame__GetScriptName(this: *const StackFrame) -> *const String;
57  fn v8__StackFrame__GetScriptNameOrSourceURL(
58    this: *const StackFrame,
59  ) -> *const String;
60  fn v8__StackFrame__GetScriptSource(this: *const StackFrame) -> *const String;
61  fn v8__StackFrame__GetScriptSourceMappingURL(
62    this: *const StackFrame,
63  ) -> *const String;
64  fn v8__StackFrame__GetFunctionName(this: *const StackFrame) -> *const String;
65  fn v8__StackFrame__IsEval(this: *const StackFrame) -> bool;
66  fn v8__StackFrame__IsConstructor(this: *const StackFrame) -> bool;
67  fn v8__StackFrame__IsWasm(this: *const StackFrame) -> bool;
68  fn v8__StackFrame__IsUserJavaScript(this: *const StackFrame) -> bool;
69
70  fn v8__Exception__Error(message: *const String) -> *const Value;
71  fn v8__Exception__RangeError(message: *const String) -> *const Value;
72  fn v8__Exception__ReferenceError(message: *const String) -> *const Value;
73  fn v8__Exception__SyntaxError(message: *const String) -> *const Value;
74  fn v8__Exception__TypeError(message: *const String) -> *const Value;
75
76  fn v8__Exception__CreateMessage(
77    isolate: *mut RealIsolate,
78    exception: *const Value,
79  ) -> *const Message;
80  fn v8__Exception__GetStackTrace(exception: *const Value)
81  -> *const StackTrace;
82  fn v8__Exception__CaptureStackTrace(
83    context: *const Context,
84    object: *const Object,
85  ) -> MaybeBool;
86}
87
88impl StackTrace {
89  /// Grab a snapshot of the current JavaScript execution stack.
90  #[inline(always)]
91  pub fn current_stack_trace<'s>(
92    scope: &PinScope<'s, '_>,
93    frame_limit: usize,
94  ) -> Option<Local<'s, StackTrace>> {
95    let frame_limit = frame_limit.try_into().ok()?;
96    unsafe {
97      scope.cast_local(|sd| {
98        v8__StackTrace__CurrentStackTrace(sd.get_isolate_ptr(), frame_limit)
99      })
100    }
101  }
102
103  /// Returns the first valid script name or source URL starting at the top of
104  /// the JS stack. The returned string is either an empty handle if no script
105  /// name/url was found or a non-zero-length string.
106  ///
107  /// This method is equivalent to calling StackTrace::CurrentStackTrace and
108  /// walking the resulting frames from the beginning until a non-empty script
109  /// name/url is found. The difference is that this method won't allocate
110  /// a stack trace.
111  ///
112  #[inline(always)]
113  pub fn current_script_name_or_source_url<'s>(
114    scope: &PinScope<'s, '_>,
115  ) -> Option<Local<'s, String>> {
116    unsafe {
117      scope.cast_local(|sd| {
118        v8__StackTrace__CurrentScriptNameOrSourceURL(sd.get_isolate_ptr())
119      })
120    }
121  }
122
123  /// Returns the number of StackFrames.
124  #[inline(always)]
125  pub fn get_frame_count(&self) -> usize {
126    unsafe { v8__StackTrace__GetFrameCount(self) as usize }
127  }
128
129  /// Returns a StackFrame at a particular index.
130  #[inline(always)]
131  pub fn get_frame<'s>(
132    &self,
133    scope: &PinScope<'s, '_>,
134    index: usize,
135  ) -> Option<Local<'s, StackFrame>> {
136    unsafe {
137      scope.cast_local(|sd| {
138        v8__StackTrace__GetFrame(self, sd.get_isolate_ptr(), index as u32)
139      })
140    }
141  }
142}
143
144impl StackFrame {
145  /// Returns the number, 1-based, of the line for the associated function call.
146  /// This method will return Message::kNoLineNumberInfo if it is unable to
147  /// retrieve the line number, or if kLineNumber was not passed as an option
148  /// when capturing the StackTrace.
149  #[inline(always)]
150  pub fn get_line_number(&self) -> usize {
151    unsafe { v8__StackFrame__GetLineNumber(self) as usize }
152  }
153
154  /// Returns the 1-based column offset on the line for the associated function
155  /// call.
156  /// This method will return Message::kNoColumnInfo if it is unable to retrieve
157  /// the column number, or if kColumnOffset was not passed as an option when
158  /// capturing the StackTrace.
159  #[inline(always)]
160  pub fn get_column(&self) -> usize {
161    unsafe { v8__StackFrame__GetColumn(self) as usize }
162  }
163
164  /// Returns the id of the script for the function for this StackFrame.
165  /// This method will return Message::kNoScriptIdInfo if it is unable to
166  /// retrieve the script id, or if kScriptId was not passed as an option when
167  /// capturing the StackTrace.
168  #[inline(always)]
169  pub fn get_script_id(&self) -> usize {
170    unsafe { v8__StackFrame__GetScriptId(self) as usize }
171  }
172
173  /// Returns the name of the resource that contains the script for the
174  /// function for this StackFrame.
175  #[inline(always)]
176  pub fn get_script_name<'s>(
177    &self,
178    scope: &PinScope<'s, '_>,
179  ) -> Option<Local<'s, String>> {
180    unsafe { scope.cast_local(|_| v8__StackFrame__GetScriptName(self)) }
181  }
182
183  /// Returns the name of the resource that contains the script for the
184  /// function for this StackFrame or sourceURL value if the script name
185  /// is undefined and its source ends with //# sourceURL=... string or
186  /// deprecated //@ sourceURL=... string.
187  #[inline(always)]
188  pub fn get_script_name_or_source_url<'s>(
189    &self,
190    scope: &PinScope<'s, '_>,
191  ) -> Option<Local<'s, String>> {
192    unsafe {
193      scope.cast_local(|_| v8__StackFrame__GetScriptNameOrSourceURL(self))
194    }
195  }
196
197  /// Returns the source of the script for the function for this StackFrame.
198  #[inline(always)]
199  pub fn get_script_source<'s>(
200    &self,
201    scope: &PinScope<'s, '_>,
202  ) -> Option<Local<'s, String>> {
203    unsafe { scope.cast_local(|_| v8__StackFrame__GetScriptSource(self)) }
204  }
205
206  /// Returns the source mapping URL (if one is present) of the script for
207  /// the function for this StackFrame.
208  #[inline(always)]
209  pub fn get_script_source_mapping_url<'s>(
210    &self,
211    scope: &PinScope<'s, '_>,
212  ) -> Option<Local<'s, String>> {
213    unsafe {
214      scope.cast_local(|_| v8__StackFrame__GetScriptSourceMappingURL(self))
215    }
216  }
217
218  /// Returns the name of the function associated with this stack frame.
219  #[inline(always)]
220  pub fn get_function_name<'s>(
221    &self,
222    scope: &PinScope<'s, '_>,
223  ) -> Option<Local<'s, String>> {
224    unsafe { scope.cast_local(|_| v8__StackFrame__GetFunctionName(self)) }
225  }
226
227  /// Returns whether or not the associated function is compiled via a call to
228  /// eval().
229  #[inline(always)]
230  pub fn is_eval(&self) -> bool {
231    unsafe { v8__StackFrame__IsEval(self) }
232  }
233
234  /// Returns whether or not the associated function is called as a
235  /// constructor via "new".
236  #[inline(always)]
237  pub fn is_constructor(&self) -> bool {
238    unsafe { v8__StackFrame__IsConstructor(self) }
239  }
240
241  /// Returns whether or not the associated functions is defined in wasm.
242  #[inline(always)]
243  pub fn is_wasm(&self) -> bool {
244    unsafe { v8__StackFrame__IsWasm(self) }
245  }
246
247  /// Returns whether or not the associated function is defined by the user.
248  #[inline(always)]
249  pub fn is_user_javascript(&self) -> bool {
250    unsafe { v8__StackFrame__IsUserJavaScript(self) }
251  }
252}
253
254impl Message {
255  #[inline(always)]
256  pub fn get<'s>(&self, scope: &PinScope<'s, '_>) -> Local<'s, String> {
257    unsafe { scope.cast_local(|_| v8__Message__Get(self)) }.unwrap()
258  }
259
260  /// Exception stack trace. By default stack traces are not captured for
261  /// uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows
262  /// to change this option.
263  #[inline(always)]
264  pub fn get_stack_trace<'s>(
265    &self,
266    scope: &PinScope<'s, '_>,
267  ) -> Option<Local<'s, StackTrace>> {
268    unsafe { scope.cast_local(|_| v8__Message__GetStackTrace(self)) }
269  }
270
271  #[inline(always)]
272  pub fn get_source_line<'s>(
273    &self,
274    scope: &PinScope<'s, '_>,
275  ) -> Option<Local<'s, String>> {
276    unsafe {
277      scope.cast_local(|sd| {
278        v8__Message__GetSourceLine(self, sd.get_current_context())
279      })
280    }
281  }
282
283  /// Returns the resource name for the script from where the function causing
284  /// the error originates.
285  #[inline(always)]
286  pub fn get_script_resource_name<'s>(
287    &self,
288    scope: &PinScope<'s, '_>,
289  ) -> Option<Local<'s, Value>> {
290    unsafe { scope.cast_local(|_| v8__Message__GetScriptResourceName(self)) }
291  }
292
293  /// Returns the number, 1-based, of the line where the error occurred.
294  #[inline(always)]
295  pub fn get_line_number(&self, scope: &PinScope<'_, '_>) -> Option<usize> {
296    let i = unsafe {
297      v8__Message__GetLineNumber(self, &*scope.get_current_context())
298    };
299    if i < 0 { None } else { Some(i as usize) }
300  }
301
302  /// Returns the index within the script of the first character where
303  /// the error occurred.
304  #[inline(always)]
305  pub fn get_start_position(&self) -> int {
306    unsafe { v8__Message__GetStartPosition(self) }
307  }
308
309  /// Returns the index within the script of the last character where
310  /// the error occurred.
311  #[inline(always)]
312  pub fn get_end_position(&self) -> int {
313    unsafe { v8__Message__GetEndPosition(self) }
314  }
315
316  /// Returns the Wasm function index where the error occurred. Returns -1 if
317  /// message is not from a Wasm script.
318  #[inline(always)]
319  pub fn get_wasm_function_index(&self) -> int {
320    unsafe { v8__Message__GetWasmFunctionIndex(self) }
321  }
322
323  /// Returns the error level of the message.
324  #[inline(always)]
325  pub fn error_level(&self) -> int {
326    unsafe { v8__Message__ErrorLevel(self) }
327  }
328
329  /// Returns the index within the line of the first character where
330  /// the error occurred.
331  #[inline(always)]
332  pub fn get_start_column(&self) -> usize {
333    unsafe { v8__Message__GetStartColumn(self) as usize }
334  }
335
336  /// Returns the index within the line of the last character where
337  /// the error occurred.
338  #[inline(always)]
339  pub fn get_end_column(&self) -> usize {
340    unsafe { v8__Message__GetEndColumn(self) as usize }
341  }
342
343  /// Passes on the value set by the embedder when it fed the script from which
344  /// this Message was generated to V8.
345  #[inline(always)]
346  pub fn is_shared_cross_origin(&self) -> bool {
347    unsafe { v8__Message__IsSharedCrossOrigin(self) }
348  }
349
350  #[inline(always)]
351  pub fn is_opaque(&self) -> bool {
352    unsafe { v8__Message__IsOpaque(self) }
353  }
354}
355
356/// Create new error objects by calling the corresponding error object
357/// constructor with the message.
358#[derive(Debug)]
359pub struct Exception;
360
361impl Exception {
362  #[inline(always)]
363  pub fn error<'s>(
364    scope: &PinScope<'s, '_>,
365    message: Local<String>,
366  ) -> Local<'s, Value> {
367    Self::new_error_with(scope, message, v8__Exception__Error)
368  }
369
370  #[inline(always)]
371  pub fn range_error<'s>(
372    scope: &PinScope<'s, '_>,
373    message: Local<String>,
374  ) -> Local<'s, Value> {
375    Self::new_error_with(scope, message, v8__Exception__RangeError)
376  }
377
378  #[inline(always)]
379  pub fn reference_error<'s>(
380    scope: &PinScope<'s, '_>,
381    message: Local<String>,
382  ) -> Local<'s, Value> {
383    Self::new_error_with(scope, message, v8__Exception__ReferenceError)
384  }
385
386  #[inline(always)]
387  pub fn syntax_error<'s>(
388    scope: &PinScope<'s, '_>,
389    message: Local<String>,
390  ) -> Local<'s, Value> {
391    Self::new_error_with(scope, message, v8__Exception__SyntaxError)
392  }
393
394  #[inline(always)]
395  pub fn type_error<'s>(
396    scope: &PinScope<'s, '_>,
397    message: Local<String>,
398  ) -> Local<'s, Value> {
399    Self::new_error_with(scope, message, v8__Exception__TypeError)
400  }
401
402  /// Internal helper to make the above error constructors less repetitive.
403  #[inline(always)]
404  fn new_error_with<'s>(
405    scope: &PinScope<'s, '_>,
406    message: Local<String>,
407    contructor: unsafe extern "C" fn(*const String) -> *const Value,
408  ) -> Local<'s, Value> {
409    unsafe {
410      scope.enter();
411      let error = scope.cast_local(|_| (contructor)(&*message)).unwrap();
412      scope.exit();
413      error
414    }
415  }
416
417  /// Creates an error message for the given exception.
418  /// Will try to reconstruct the original stack trace from the exception value,
419  /// or capture the current stack trace if not available.
420  #[inline(always)]
421  pub fn create_message<'s>(
422    scope: &PinScope<'s, '_>,
423    exception: Local<Value>,
424  ) -> Local<'s, Message> {
425    unsafe {
426      scope.cast_local(|sd| {
427        v8__Exception__CreateMessage(sd.get_isolate_ptr(), &*exception)
428      })
429    }
430    .unwrap()
431  }
432
433  /// Returns the original stack trace that was captured at the creation time
434  /// of a given exception, or an empty handle if not available.
435  #[inline(always)]
436  pub fn get_stack_trace<'s>(
437    scope: &PinScope<'s, '_>,
438    exception: Local<Value>,
439  ) -> Option<Local<'s, StackTrace>> {
440    unsafe { scope.cast_local(|_| v8__Exception__GetStackTrace(&*exception)) }
441  }
442
443  /// Captures the current stack trace and attaches it to the given object in the
444  ///  form of `stack` property.
445  #[inline(always)]
446  pub fn capture_stack_trace(
447    context: Local<Context>,
448    object: Local<Object>,
449  ) -> Option<bool> {
450    unsafe { v8__Exception__CaptureStackTrace(&*context, &*object).into() }
451  }
452}