1
1
//! Utilities for error handling when dealing with V8.
2
2
3
+ use crate :: database_logger:: { BacktraceFrame , BacktraceProvider , ModuleBacktrace } ;
4
+
5
+ use super :: serialize_to_js;
3
6
use core:: fmt;
7
+ use spacetimedb_sats:: Serialize ;
4
8
use v8:: { Exception , HandleScope , Local , StackFrame , StackTrace , TryCatch , Value } ;
5
9
6
10
/// 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> {
58
62
}
59
63
}
60
64
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
+
61
119
#[ derive( Debug ) ]
62
120
pub ( crate ) struct ExceptionThrown {
63
121
_priv : ( ) ,
@@ -134,14 +192,14 @@ impl fmt::Display for JsError {
134
192
}
135
193
136
194
/// A V8 stack trace that is independent of a `'scope`.
137
- #[ derive( Debug , Default ) ]
195
+ #[ derive( Debug , Default , Clone ) ]
138
196
pub ( super ) struct JsStackTrace {
139
197
frames : Box < [ JsStackTraceFrame ] > ,
140
198
}
141
199
142
200
impl JsStackTrace {
143
201
/// 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 {
145
203
let frames = ( 0 ..trace. get_frame_count ( ) )
146
204
. map ( |index| {
147
205
let frame = trace. get_frame ( scope, index) . unwrap ( ) ;
@@ -150,6 +208,12 @@ impl JsStackTrace {
150
208
. collect :: < Box < [ _ ] > > ( ) ;
151
209
Self { frames }
152
210
}
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
+ }
153
217
}
154
218
155
219
impl fmt:: Display for JsStackTrace {
@@ -162,8 +226,26 @@ impl fmt::Display for JsStackTrace {
162
226
}
163
227
}
164
228
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
+
165
247
/// A V8 stack trace frame that is independent of a `'scope`.
166
- #[ derive( Debug ) ]
248
+ #[ derive( Debug , Clone ) ]
167
249
pub ( super ) struct JsStackTraceFrame {
168
250
line : usize ,
169
251
column : usize ,
@@ -197,12 +279,22 @@ impl JsStackTraceFrame {
197
279
is_user_js : frame. is_user_javascript ( ) ,
198
280
}
199
281
}
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
+ }
200
292
}
201
293
202
294
impl fmt:: Display for JsStackTraceFrame {
203
295
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 ( ) ;
206
298
207
299
// This isn't exactly the same format as chrome uses,
208
300
// but it's close enough for now.
@@ -267,7 +359,8 @@ pub(super) fn catch_exception<'scope, T>(
267
359
body : impl FnOnce ( & mut HandleScope < ' scope > ) -> Result < T , ErrorOrException < ExceptionThrown > > ,
268
360
) -> Result < T , ErrorOrException < JsError > > {
269
361
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 {
271
364
ErrorOrException :: Err ( e) => ErrorOrException :: Err ( e) ,
272
365
ErrorOrException :: Exception ( _) => ErrorOrException :: Exception ( JsError :: from_caught ( scope) ) ,
273
366
} )
0 commit comments