Node.js: If a JS callback throws an exception, report the exception t… (#6416)

As a drive-by, this introduces a macro that allows for explicit logging to console.error (which goes to stderr). This is something we should gradually start using, as it allows for capturing on the Node.js side (by the user or our tests).
This commit is contained in:
Simon Hausmann 2024-10-01 13:57:50 +02:00 committed by GitHub
parent c5954f61c0
commit 51a241edd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 9 deletions

View file

@ -145,17 +145,20 @@ impl JsComponentInstance {
return Value::Void;
};
let Ok(result) = callback
let result = match callback
.call(
None,
args.iter()
.map(|v| super::value::to_js_unknown(&env, v).unwrap())
.collect::<Vec<JsUnknown>>()
.as_ref()
) else {
eprintln!("Node.js: cannot call callback {}", callback_name);
) {
Ok(result) => result,
Err(err) => {
crate::console_err!(env, "Node.js: Invoking callback '{callback_name}' failed: {err}");
return Value::Void;
};
}
};
if let Some(return_type) = &return_type {
if let Ok(value) = super::to_value(&env, result, return_type) {
@ -219,16 +222,19 @@ impl JsComponentInstance {
return Value::Void;
};
let Ok(result) = callback
let result = match callback
.call(
None,
args.iter()
.map(|v| super::value::to_js_unknown(&env, v).unwrap())
.collect::<Vec<JsUnknown>>()
.as_ref()
) else {
eprintln!("Node.js: cannot call callback {} of global {}", callback_name, global_name);
return Value::Void;
) {
Ok(result) => result,
Err(err) => {
crate::console_err!(env, "Node.js: Invoking global callback '{callback_name}' failed: {err}");
return Value::Void;
}
};
if let Some(return_type) = &return_type {

View file

@ -7,7 +7,7 @@ pub use interpreter::*;
mod types;
pub use types::*;
use napi::{Env, JsFunction};
use napi::{Env, JsFunction, JsObject};
#[macro_use]
extern crate napi_derive;
@ -85,3 +85,38 @@ pub fn init_testing() {
#[cfg(feature = "testing")]
i_slint_backend_testing::init_integration_test_with_mock_time();
}
pub fn print_to_console(env: Env, function: &str, arguments: core::fmt::Arguments) {
let Ok(global) = env.get_global() else {
eprintln!("Unable to obtain global object");
return;
};
let Ok(console_object) = global
.get_named_property::<JsObject>("console")
.and_then(|console| console.coerce_to_object())
else {
eprintln!("Unable to obtain console object for logging");
return;
};
let Ok(Some(log_fn)) = console_object.get::<&str, JsFunction>(function) else {
eprintln!("Unable to obtain console.{function}");
return;
};
let message = arguments.to_string();
let Ok(js_message) = env.create_string(&message) else {
eprintln!("Unable to provide log message to JS env");
return;
};
if let Err(err) = log_fn.call(None, &vec![js_message.into_unknown()]) {
eprintln!("Unable to invoke console.{function}: {err}");
}
}
#[macro_export]
macro_rules! console_err {
($env:expr, $($t:tt)*) => ($crate::print_to_console($env, "error", format_args!($($t)*)))
}