Skip to content

Commit 7acfdfd

Browse files
improve error to string
1 parent 9687d24 commit 7acfdfd

File tree

4 files changed

+92
-12
lines changed

4 files changed

+92
-12
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
# 0.15.7
2+
3+
* updated error handling / toString (include cause and such)
4+
5+
# 0.15.6
6+
7+
* minor textual update to unhandled promise rejection logging
8+
19
# 0.15.5
210

3-
* unhandled promise rejection stacktrace fixed (typescript transpiled)
11+
* unhandled promise rejection stacktrace fixed (typescript transpiled)
412

513
# 0.15.4
614

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "quickjs_runtime"
3-
version = "0.15.6"
3+
version = "0.15.7"
44
authors = ["Andries Hiemstra <[email protected]>"]
55
edition = "2021"
66
description = "Wrapper API and utils for the QuickJS JavaScript engine with support for Promise, Modules, Async/await"
@@ -56,7 +56,7 @@ anyhow = "1"
5656
swc = { version = "=35.0.0", optional = true }
5757
swc_config = { version = "=3.1.2", optional = true }
5858
swc_config_macro = { version = "=1.0.1", optional = true }
59-
swc_ecma_ast = { version = "=15.0.0", features = ["serde-impl"] , optional=true}
59+
swc_ecma_ast = { version = "=15.0.0", features = ["serde-impl"], optional = true }
6060
swc_atoms = { version = "=7.0.0", optional = true }
6161
swc_cached = { version = "=2.0.0", optional = true }
6262
swc_eq_ignore_macros = { version = "=1.0.1", optional = true }
@@ -65,8 +65,8 @@ swc_common = { version = "=14.0.4", optional = true, features = ["tty-emitter"]
6565
swc_ecma_codegen = { version = "=17.0.0", optional = true }
6666
swc_ecma_codegen_macros = { version = "=2.0.2", optional = true }
6767
swc_ecma_loader = { version = "=14.0.0", optional = true }
68-
swc_ecma_lexer = { version="=23.0.1" , optional=true}
69-
swc_ecma_parser = {version="=23.0.0", optional=true}
68+
swc_ecma_lexer = { version = "=23.0.1", optional = true }
69+
swc_ecma_parser = { version = "=23.0.0", optional = true }
7070
swc_ecma_transforms_base = { version = "=25.0.0", features = ["inline-helpers"], optional = true }
7171
swc_ecma_transforms_classes = { version = "=25.0.0", optional = true }
7272
swc_ecma_transforms_compat = { version = "=27.0.0", optional = true }

src/jsutils/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! The facade classes are for use outside the worker thread, they are Send
44
//!
55
6+
use crate::values::JsValueFacade;
67
use backtrace::Backtrace;
78
use std::fmt::{Debug, Display, Error, Formatter};
89

@@ -58,6 +59,7 @@ pub struct JsError {
5859
name: String,
5960
message: String,
6061
stack: String,
62+
cause: Option<Box<JsValueFacade>>,
6163
}
6264

6365
impl JsError {
@@ -66,6 +68,15 @@ impl JsError {
6668
name,
6769
message,
6870
stack,
71+
cause: None,
72+
}
73+
}
74+
pub fn new2(name: String, message: String, stack: String, cause: JsValueFacade) -> Self {
75+
Self {
76+
name,
77+
message,
78+
stack,
79+
cause: Some(Box::new(cause)),
6980
}
7081
}
7182
pub fn new_str(err: &str) -> Self {
@@ -77,6 +88,7 @@ impl JsError {
7788
name: "Error".to_string(),
7889
message: err,
7990
stack: format!("{bt:?}"),
91+
cause: None,
8092
}
8193
}
8294
pub fn get_message(&self) -> &str {
@@ -88,6 +100,9 @@ impl JsError {
88100
pub fn get_name(&self) -> &str {
89101
self.name.as_str()
90102
}
103+
pub fn get_cause(&self) -> &Option<Box<JsValueFacade>> {
104+
&self.cause
105+
}
91106
}
92107

93108
impl std::error::Error for JsError {
@@ -98,7 +113,7 @@ impl std::error::Error for JsError {
98113

99114
impl From<anyhow::Error> for JsError {
100115
fn from(err: anyhow::Error) -> Self {
101-
JsError::new_string(err.to_string())
116+
JsError::new_string(format!("{err:?}"))
102117
}
103118
}
104119
impl std::fmt::Display for JsError {
@@ -110,7 +125,7 @@ impl std::fmt::Display for JsError {
110125

111126
impl From<Error> for JsError {
112127
fn from(e: Error) -> Self {
113-
JsError::new_string(format!("{e}"))
128+
JsError::new_string(format!("{e:?}"))
114129
}
115130
}
116131

src/quickjs_utils/errors.rs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ pub unsafe fn get_exception(context: *mut q::JSContext) -> Option<JsError> {
2424
} else if exception_ref.is_object() {
2525
error_to_js_error(context, &exception_ref)
2626
} else {
27-
JsError::new_string(format!(
28-
"no clue what happened {}",
29-
exception_ref.get_js_type()
30-
))
27+
match exception_ref.to_string() {
28+
Ok(s) => JsError::new_string(s),
29+
Err(ex) => {
30+
JsError::new_string(format!("Could not determine error due to error: {ex:?}"))
31+
}
32+
}
3133
};
3234
Some(err)
3335
}
@@ -74,7 +76,24 @@ pub unsafe fn error_to_js_error(
7476
stack_string.push_str(stack_str.as_str());
7577
}
7678

77-
JsError::new(name_string, message_string, stack_string)
79+
let cause_ref = objects::get_property(context, exception_ref, "cause")
80+
.ok()
81+
.unwrap();
82+
// add cause as cause but also extend the stack trace with the cause stack trace
83+
84+
if cause_ref.is_null_or_undefined() {
85+
JsError::new(name_string, message_string, stack_string)
86+
} else {
87+
QuickJsRealmAdapter::with_context(context, |realm| {
88+
let cause_str = cause_ref.to_string().ok().unwrap();
89+
let cause_jsvf = realm.to_js_value_facade(&cause_ref).ok().unwrap();
90+
91+
stack_string.push_str("Caused by: ");
92+
stack_string.push_str(cause_str.as_str());
93+
94+
JsError::new2(name_string, message_string, stack_string, cause_jsvf)
95+
})
96+
}
7897
}
7998

8099
/// Create a new Error object
@@ -185,6 +204,44 @@ pub mod tests {
185204
assert_eq!(ex.get_message(), "__c_v__ is not defined");
186205
}
187206

207+
#[test]
208+
fn test_ex_cause() {
209+
// check if stacktrace is preserved when invoking native methods
210+
211+
let rt = init_test_rt();
212+
let res = rt.eval_sync(
213+
None,
214+
Script::new(
215+
"ex.ts",
216+
r#"
217+
let a = 2;
218+
let b = 3;
219+
type Foo = {
220+
a: number
221+
}
222+
function f1(a, b) {
223+
throw new Error('Could not f1', { cause: 'Sabotage here' });
224+
}
225+
function f2() {
226+
try {
227+
let r = f1(a, b);
228+
} catch(ex) {
229+
throw new Error('could not f2', { cause: ex});
230+
}
231+
}
232+
f2()
233+
"#,
234+
),
235+
);
236+
let ex = res.expect_err("script should have failed;");
237+
238+
assert_eq!(ex.get_message(), "could not f2");
239+
240+
let complete_err = format!("{ex}");
241+
assert!(complete_err.contains("Caused by: Error: Could not f1"));
242+
assert!(complete_err.contains("Caused by: Sabotage here"));
243+
}
244+
188245
#[test]
189246
fn test_ex0() {
190247
// check if stacktrace is preserved when invoking native methods

0 commit comments

Comments
 (0)