11//! Utilities for error handling when dealing with V8.
22
3+ use crate :: database_logger:: { BacktraceFrame , BacktraceProvider , ModuleBacktrace } ;
4+
5+ use super :: serialize_to_js;
36use core:: fmt;
7+ use spacetimedb_sats:: Serialize ;
48use v8:: { Exception , HandleScope , Local , StackFrame , StackTrace , TryCatch , Value } ;
59
610/// The result of trying to convert a [`Value`] in scope `'scope` to some type `T`.
@@ -58,6 +62,60 @@ impl<'scope, M: IntoJsString> IntoException<'scope> for RangeError<M> {
5862 }
5963}
6064
65+ /// A catchable termination error thrown in callbacks to indicate a host error.
66+ #[ derive( Serialize ) ]
67+ pub ( super ) struct TerminationError {
68+ __terminated__ : String ,
69+ }
70+
71+ impl TerminationError {
72+ /// Convert `anyhow::Error` to a termination error.
73+ pub ( super ) fn from_error < ' scope > (
74+ scope : & mut HandleScope < ' scope > ,
75+ error : & anyhow:: Error ,
76+ ) -> ExcResult < ExceptionValue < ' scope > > {
77+ let __terminated__ = format ! ( "{error}" ) ;
78+ let error = Self { __terminated__ } ;
79+ serialize_to_js ( scope, & error) . map ( ExceptionValue )
80+ }
81+ }
82+
83+ /// A catchable error code thrown in callbacks
84+ /// to indicate bad arguments to a syscall.
85+ #[ derive( Serialize ) ]
86+ pub ( super ) struct CodeError {
87+ __code_error__ : u16 ,
88+ }
89+
90+ impl CodeError {
91+ /// Create a code error from a code.
92+ pub ( super ) fn from_code < ' scope > (
93+ scope : & mut HandleScope < ' scope > ,
94+ __code_error__ : u16 ,
95+ ) -> ExcResult < ExceptionValue < ' scope > > {
96+ let error = Self { __code_error__ } ;
97+ serialize_to_js ( scope, & error) . map ( ExceptionValue )
98+ }
99+ }
100+
101+ /// A catchable error code thrown in callbacks
102+ /// to indicate that a buffer was too small and the minimum size required.
103+ #[ derive( Serialize ) ]
104+ pub ( super ) struct BufferTooSmall {
105+ __buffer_too_small__ : u32 ,
106+ }
107+
108+ impl BufferTooSmall {
109+ /// Create a code error from a code.
110+ pub ( super ) fn from_requirement < ' scope > (
111+ scope : & mut HandleScope < ' scope > ,
112+ __buffer_too_small__ : u32 ,
113+ ) -> ExcResult < ExceptionValue < ' scope > > {
114+ let error = Self { __buffer_too_small__ } ;
115+ serialize_to_js ( scope, & error) . map ( ExceptionValue )
116+ }
117+ }
118+
61119#[ derive( Debug ) ]
62120pub ( crate ) struct ExceptionThrown {
63121 _priv : ( ) ,
@@ -134,14 +192,14 @@ impl fmt::Display for JsError {
134192}
135193
136194/// A V8 stack trace that is independent of a `'scope`.
137- #[ derive( Debug , Default ) ]
195+ #[ derive( Debug , Default , Clone ) ]
138196pub ( super ) struct JsStackTrace {
139197 frames : Box < [ JsStackTraceFrame ] > ,
140198}
141199
142200impl JsStackTrace {
143201 /// Converts a V8 [`StackTrace`] into one independent of `'scope`.
144- fn from_trace < ' scope > ( scope : & mut HandleScope < ' scope > , trace : Local < ' scope , StackTrace > ) -> Self {
202+ pub ( super ) fn from_trace < ' scope > ( scope : & mut HandleScope < ' scope > , trace : Local < ' scope , StackTrace > ) -> Self {
145203 let frames = ( 0 ..trace. get_frame_count ( ) )
146204 . map ( |index| {
147205 let frame = trace. get_frame ( scope, index) . unwrap ( ) ;
@@ -150,6 +208,12 @@ impl JsStackTrace {
150208 . collect :: < Box < [ _ ] > > ( ) ;
151209 Self { frames }
152210 }
211+
212+ /// Construct a backtrace from `scope`.
213+ pub ( super ) fn from_current_stack_trace ( scope : & mut HandleScope < ' _ > ) -> ExcResult < Self > {
214+ let trace = StackTrace :: current_stack_trace ( scope, 1024 ) . ok_or_else ( exception_already_thrown) ?;
215+ Ok ( Self :: from_trace ( scope, trace) )
216+ }
153217}
154218
155219impl fmt:: Display for JsStackTrace {
@@ -162,8 +226,26 @@ impl fmt::Display for JsStackTrace {
162226 }
163227}
164228
229+ impl BacktraceProvider for JsStackTrace {
230+ fn capture ( & self ) -> Box < dyn ModuleBacktrace > {
231+ Box :: new ( self . clone ( ) )
232+ }
233+ }
234+
235+ impl ModuleBacktrace for JsStackTrace {
236+ fn frames ( & self ) -> Vec < BacktraceFrame < ' _ > > {
237+ self . frames
238+ . iter ( )
239+ . map ( |frame| BacktraceFrame {
240+ module_name : frame. script_name . as_deref ( ) ,
241+ func_name : frame. fn_name . as_deref ( ) ,
242+ } )
243+ . collect ( )
244+ }
245+ }
246+
165247/// A V8 stack trace frame that is independent of a `'scope`.
166- #[ derive( Debug ) ]
248+ #[ derive( Debug , Clone ) ]
167249pub ( super ) struct JsStackTraceFrame {
168250 line : usize ,
169251 column : usize ,
@@ -197,12 +279,22 @@ impl JsStackTraceFrame {
197279 is_user_js : frame. is_user_javascript ( ) ,
198280 }
199281 }
282+
283+ /// Returns the name of the function that was called.
284+ fn fn_name ( & self ) -> & str {
285+ self . fn_name . as_deref ( ) . unwrap_or ( "<anonymous>" )
286+ }
287+
288+ /// Returns the name of the script where the function resides.
289+ fn script_name ( & self ) -> & str {
290+ self . script_name . as_deref ( ) . unwrap_or ( "<unknown location>" )
291+ }
200292}
201293
202294impl fmt:: Display for JsStackTraceFrame {
203295 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
204- let fn_name = self . fn_name . as_deref ( ) . unwrap_or ( "<anonymous>" ) ;
205- let script_name = self . script_name . as_deref ( ) . unwrap_or ( "<unknown location>" ) ;
296+ let fn_name = self . fn_name ( ) ;
297+ let script_name = self . script_name ( ) ;
206298
207299 // This isn't exactly the same format as chrome uses,
208300 // but it's close enough for now.
@@ -267,7 +359,8 @@ pub(super) fn catch_exception<'scope, T>(
267359 body : impl FnOnce ( & mut HandleScope < ' scope > ) -> Result < T , ErrorOrException < ExceptionThrown > > ,
268360) -> Result < T , ErrorOrException < JsError > > {
269361 let scope = & mut TryCatch :: new ( scope) ;
270- body ( scope) . map_err ( |e| match e {
362+ let ret = body ( scope) ;
363+ ret. map_err ( |e| match e {
271364 ErrorOrException :: Err ( e) => ErrorOrException :: Err ( e) ,
272365 ErrorOrException :: Exception ( _) => ErrorOrException :: Exception ( JsError :: from_caught ( scope) ) ,
273366 } )
0 commit comments