Skip to content

Commit 21794cf

Browse files
goffrieConvex, Inc.
authored andcommitted
Make it possible to throw UncatchableDeveloperError from Rust ops (#42765)
GitOrigin-RevId: 529f78876ca35de864578db072a4f2bba2dffd71
1 parent e1d23dd commit 21794cf

File tree

3 files changed

+47
-18
lines changed

3 files changed

+47
-18
lines changed

crates/isolate/src/ops/errors.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,62 @@ use common::errors::{
44
FrameData,
55
JsError,
66
};
7+
use deno_core::v8;
78

89
use super::OpProvider;
9-
use crate::environment::UncatchableDeveloperError;
10+
use crate::{
11+
environment::UncatchableDeveloperError,
12+
strings,
13+
};
1014

11-
#[convex_macro::v8_op]
12-
pub fn op_throw_uncatchable_developer_error<'b, P: OpProvider<'b>>(
15+
pub(crate) fn throw_uncatchable_developer_error<'b, P: OpProvider<'b>>(
1316
provider: &mut P,
1417
message: String,
15-
frame_data: Vec<FrameData>,
16-
) -> anyhow::Result<()> {
17-
let js_error = JsError::from_frames(message.clone(), frame_data, None, |s| {
18-
provider.lookup_source_map(s)
19-
});
18+
) -> anyhow::Result<!> {
19+
let frame_data: anyhow::Result<Vec<FrameData>> = try {
20+
let mut scope = v8::HandleScope::new(provider.scope());
21+
let empty_string = strings::empty.create(&mut scope)?;
22+
let error = v8::Exception::error(&mut scope, empty_string).try_cast::<v8::Object>()?;
23+
let stack_string = strings::stack.create(&mut scope)?;
24+
// This calls `prepareStackTrace` that populates `__frameData`
25+
error
26+
.get(&mut scope, stack_string.into())
27+
.context("Error.stack threw")?;
28+
let frame_data_str = strings::__frameData.create(&mut scope)?;
29+
let frame_data_json = error
30+
.get(&mut scope, frame_data_str.into())
31+
.context("Error.__frameData threw")?
32+
.try_cast::<v8::String>()?;
33+
let frame_data_json = frame_data_json.to_rust_string_lossy(&mut scope);
34+
serde_json::from_str(&frame_data_json)?
35+
};
36+
let js_error = JsError::from_frames(
37+
message.clone(),
38+
match frame_data {
39+
Ok(data) => data,
40+
Err(mut e) => {
41+
report_error_sync(&mut e);
42+
vec![]
43+
},
44+
},
45+
None,
46+
|s| provider.lookup_source_map(s),
47+
);
2048
report_error_sync(&mut anyhow::anyhow!(format!(
2149
"UncatchableDeveloperError: {}",
2250
message
2351
)));
2452
anyhow::bail!(UncatchableDeveloperError { js_error })
2553
}
2654

55+
#[convex_macro::v8_op]
56+
pub fn op_throw_uncatchable_developer_error<'b, P: OpProvider<'b>>(
57+
provider: &mut P,
58+
message: String,
59+
) -> anyhow::Result<()> {
60+
throw_uncatchable_developer_error(provider, message)?;
61+
}
62+
2763
/// Do source mapping to find the stack trace for an error.
2864
/// NOTE if a UDF throws an error, we call this op and then separately do
2965
/// source mapping again so the yielded error has structured frame data.

crates/isolate/src/strings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ macro_rules! declare_strings {
5555
// identifier as a string, or explicitly name the string with the `$name =>
5656
// $string` syntax.
5757
declare_strings!(
58+
__frameData,
5859
_handler,
5960
_onInitCallbacks,
6061
Convex,
@@ -83,5 +84,6 @@ declare_strings!(
8384
path,
8485
runRequest,
8586
setup,
87+
stack,
8688
syscall,
8789
);

npm-packages/udf-runtime/src/helpers.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,7 @@ export const throwNotImplementedError = (
4444
};
4545

4646
export const throwUncatchableDeveloperError = (message: string): never => {
47-
// Make an error object so we can grab its stack trace and pass it through to
48-
// the syscall
49-
const error = new Error();
50-
// This calls `prepareStackTrace` that populates `__frameData`
51-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
52-
error.stack;
53-
const frameData = JSON.parse((error as any).__frameData ?? []);
54-
performOp("throwUncatchableDeveloperError", message, frameData);
55-
// This is not actually reachable because the syscall above will throw
56-
return null as never;
47+
return performOp("throwUncatchableDeveloperError", message) as never;
5748
};
5849

5950
export function requiredArguments(length, required, prefix) {

0 commit comments

Comments
 (0)