diff --git a/muse-lang/src/runtime/list.rs b/muse-lang/src/runtime/list.rs index eed6a6c..0cd1e97 100644 --- a/muse-lang/src/runtime/list.rs +++ b/muse-lang/src/runtime/list.rs @@ -44,7 +44,7 @@ pub static LIST_TYPE: RustType = RustType::new("List", |t| { 1, |vm, this| { let key = vm[Register(0)].take(); - this.get(&key) + this.get_by_value(&key) }, ), ) @@ -67,13 +67,17 @@ impl List { /// /// Returns [`Fault::OutOfBounds`] if `index` cannot be converted to a /// `usize` or is out of bounds of this list. - pub fn get(&self, index: &Value) -> Result { + pub fn get_by_value(&self, index: &Value) -> Result { let Some(index) = index.as_usize() else { return Err(Fault::OutOfBounds); }; + self.get(index).ok_or(Fault::OutOfBounds) + } + + pub fn get(&self, index: usize) -> Option { let contents = self.0.lock(); - contents.get(index).copied().ok_or(Fault::OutOfBounds) + contents.get(index).copied() } /// Inserts `value` at `index`. diff --git a/muse-reactor/src/lib.rs b/muse-reactor/src/lib.rs index 4b9745e..213cc28 100644 --- a/muse-reactor/src/lib.rs +++ b/muse-reactor/src/lib.rs @@ -66,16 +66,17 @@ //! ``` #![allow(missing_docs)] use std::any::Any; +use std::backtrace::Backtrace; use std::cell::Cell; use std::collections::VecDeque; -use std::fmt::Debug; +use std::fmt::{Debug, Write}; use std::future::Future; use std::marker::PhantomData; use std::num::NonZeroUsize; -use std::panic::{self, AssertUnwindSafe}; +use std::panic::{self, AssertUnwindSafe, PanicInfo}; use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use std::task::{Context, Poll, Wake, Waker}; use std::thread::{self, JoinHandle}; use std::time::{Duration, Instant}; @@ -105,6 +106,11 @@ extern crate tracing; #[macro_use] mod mock_tracing; +static PANIC_HOOK_INSTALL: OnceLock<()> = OnceLock::new(); +thread_local! { + static PANIC_INFO: Cell)>> = Cell::new(None); +} + pub struct Builder { vm_source: Option>>, threads: usize, @@ -153,6 +159,14 @@ where } pub fn finish(self) -> ReactorHandle { + PANIC_HOOK_INSTALL.get_or_init(|| { + let default_hook = panic::take_hook(); + panic::set_hook(Box::new(move |info: &PanicInfo| { + PANIC_INFO.set(Some((info.to_string(), Some(Backtrace::capture())))); + default_hook(info); + })); + }); + let (sender, receiver) = if let Some(limit) = self.work_queue_limit { flume::bounded(limit) } else { @@ -581,8 +595,18 @@ where } Err(mut panic) => { drop(future); + let (mut summary, backtrace) = PANIC_INFO.take().unwrap_or_default(); + if let Some(backtrace) = backtrace { + let _result = write!(&mut summary, "\n{backtrace}"); + } let result = root_result( - Err(Value::from(SymbolRef::from("panic"))), + Err(Value::dynamic( + List::from_iter([ + Value::from(SymbolRef::from("panic")), + Value::from(SymbolRef::from(summary)), + ]), + vm_context.guard(), + )), &mut vm_context, ); drop(vm_context); diff --git a/muse-reactor/src/tests.rs b/muse-reactor/src/tests.rs index 1b60757..b3c585a 100644 --- a/muse-reactor/src/tests.rs +++ b/muse-reactor/src/tests.rs @@ -3,7 +3,8 @@ use std::time::{Duration, Instant}; use muse_lang::compiler::syntax::Ranged; use muse_lang::compiler::{self}; -use muse_lang::runtime::symbol::Symbol; +use muse_lang::runtime::list::List; +use muse_lang::runtime::symbol::SymbolRef; use muse_lang::runtime::value::{Primitive, RootedValue, RustFunction, Value}; use muse_lang::vm::Vm; use refuse::CollectionGuard; @@ -204,7 +205,14 @@ fn task_panic() { let task = reactor.spawn_source("panics()").unwrap(); let error = task.join().unwrap_err(); match error { - TaskError::Exception(exc) if exc == RootedValue::from(Symbol::from("panic")) => {} + TaskError::Exception(exc) + if exc.as_rooted::().map_or(false, |list| { + list.get(0) == Some(Value::from(SymbolRef::from("panic"))) + && list + .get(1) + .and_then(|v| v.as_symbol(&CollectionGuard::acquire())) + .map_or(false, |s| dbg!(s).contains("panicked at muse-reactor")) + }) => {} other => unreachable!("Unexpected result: {other:?}"), } }