Skip to content

Commit 5e12f1a

Browse files
committed
wip
1 parent 017f695 commit 5e12f1a

File tree

11 files changed

+1683
-461
lines changed

11 files changed

+1683
-461
lines changed

Cargo.lock

Lines changed: 334 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ unicode-normalization = "0.1.23"
291291
url = "2.3.1"
292292
urlencoding = "2.1.2"
293293
uuid = { version = "1.2.1", features = ["v4"] }
294-
v8 = "137.2"
294+
v8 = "140.0"
295295
walkdir = "2.2.5"
296296
wasmbin = "0.6"
297297
webbrowser = "1.0.2"

crates/core/src/host/instance_env.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::scheduler::{get_schedule_from_row, ScheduleError, Scheduler};
2-
use crate::database_logger::{BacktraceProvider, LogLevel, Record};
2+
use crate::database_logger::{BacktraceFrame, BacktraceProvider, LogLevel, ModuleBacktrace, Record};
33
use crate::db::relational_db::{MutTx, RelationalDB};
44
use crate::error::{DBError, DatastoreError, IndexError, NodesError};
55
use crate::host::wasm_common::TimingSpan;
@@ -197,18 +197,31 @@ impl InstanceEnv {
197197
}
198198

199199
/// End a console timer by logging the span at INFO level.
200-
pub(crate) fn console_timer_end(&self, span: &TimingSpan, bt: &dyn BacktraceProvider) {
200+
pub(crate) fn console_timer_end(&self, span: &TimingSpan) {
201201
let elapsed = span.start.elapsed();
202202
let message = format!("Timing span {:?}: {:?}", &span.name, elapsed);
203203

204+
/// A backtrace provider that provides nothing.
205+
struct Noop;
206+
impl BacktraceProvider for Noop {
207+
fn capture(&self) -> Box<dyn ModuleBacktrace> {
208+
Box::new(Noop)
209+
}
210+
}
211+
impl ModuleBacktrace for Noop {
212+
fn frames(&self) -> Vec<BacktraceFrame<'_>> {
213+
Vec::new()
214+
}
215+
}
216+
204217
let record = Record {
205218
ts: chrono::Utc::now(),
206219
target: None,
207220
filename: None,
208221
line_number: None,
209222
message: &message,
210223
};
211-
self.console_log(LogLevel::Info, &record, bt);
224+
self.console_log(LogLevel::Info, &record, &Noop);
212225
}
213226

214227
/// Project `cols` in `row_ref` encoded in BSATN to `buffer`

crates/core/src/host/v8/error.rs

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
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;
36
use core::fmt;
7+
use spacetimedb_sats::Serialize;
48
use 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)]
62120
pub(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)]
138196
pub(super) struct JsStackTrace {
139197
frames: Box<[JsStackTraceFrame]>,
140198
}
141199

142200
impl 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

155219
impl 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)]
167249
pub(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

202294
impl 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

Comments
 (0)