#![allow(unexpected_cfgs)]
#![allow(unused_imports, reason = "behavior changes in compilation context")]
#[cfg(feature = "std")]
use core::any::Any;
#[cfg(not(feature = "std"))]
use core::{
any::Any,
mem::{self, ManuallyDrop},
panic::{self, Location, PanicInfo},
};
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String};
#[cfg(not(feature = "std"))]
use io_core::io::Write;
macro_rules! rtprintpanic {
($($t:tt)*) => {
#[cfg(not(panic = "immediate-abort"))] {
print(format_args!($($t)*));
}
#[cfg(panic = "immediate-abort")]
{
let _ = format_args!($($t)*);
}
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
print_and_die(format_args!("fatal runtime error: {}, aborting\n", format_args!($($t)*)));
}
}
}
#[cfg(not(feature = "std"))]
#[allow(unused_variables, reason = "behavior changes in compilation context")]
fn print(args: core::fmt::Arguments) {
cfg_select! {
not(panic = "immediate-abort") => {
if cfg!(pbp) {
crate::io::printing::_dprint(args);
}
if let Some(mut out) = crate::os::stdio::panic_output() {
let _ = out.write_fmt(args);
}
},
_ => {}
}
}
#[cfg(not(feature = "std"))]
fn print_and_die(args: core::fmt::Arguments) -> ! {
print(args);
crate::process::abort();
}
#[inline(never)]
#[cfg(all(not(feature = "std"), panic = "immediate-abort"))]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
crate::process::abort()
}
#[inline(never)]
#[cfg(all(not(feature = "std"), panic = "abort", not(panic = "immediate-abort")))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let message = info.message();
let location = info.location().unwrap();
update_panic_count(1);
let panic_out = crate::os::stdio::panic_output();
if let Some(mut panic_out) = panic_out {
let mut buffer = [0u8; 512];
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
let write_msg = |dst: &mut dyn Write| {
writeln!(dst, "\nPSPSDK: panicked at {location}:\n\t{message}")
};
if write_msg(&mut cursor).is_ok() {
let pos = cursor.position() as usize;
let _ = panic_out.write_all(&buffer[0..pos]);
} else {
let _ = write_msg(&mut panic_out);
}
}
crate::process::abort()
}
#[inline(never)]
#[cfg(all(not(feature = "std"), panic = "unwind"))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
panic_impl(info)
}
#[inline(always)]
#[cfg_attr(not(target_os = "psp"), allow(unused))]
#[cfg(not(feature = "std"))]
#[allow(dead_code, reason = "behavior changes in compilation context")]
fn panic_impl(info: &PanicInfo) -> ! {
use core::fmt;
struct FormatStringPayload<'a> {
inner: &'a core::panic::PanicMessage<'a>,
string: Option<String>,
}
impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String {
let inner = self.inner;
self.string.get_or_insert_with(|| alloc::format!("{inner}"))
}
}
unsafe impl panic::PanicPayload for FormatStringPayload<'_> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
let contents = mem::take(self.fill());
Box::into_raw(Box::new(contents))
}
fn get(&mut self) -> &(dyn Any + Send) {
self.fill()
}
}
impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = &self.string {
f.write_str(s)
} else {
fmt::Display::fmt(&self.inner, f)
}
}
}
struct StaticStrPayload(&'static str);
unsafe impl panic::PanicPayload for StaticStrPayload {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(Box::new(self.0))
}
fn get(&mut self) -> &(dyn Any + Send) {
&self.0
}
fn as_str(&mut self) -> Option<&str> {
Some(self.0)
}
}
impl fmt::Display for StaticStrPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
let loc = info.location().unwrap(); let msg = info.message();
if let Some(s) = msg.as_str() {
rust_panic_with_hook(&mut StaticStrPayload(s), loc);
} else {
rust_panic_with_hook(
&mut FormatStringPayload {
inner: &msg,
string: None,
},
loc,
);
}
}
#[cfg(not(feature = "std"))]
fn rust_panic_with_hook(
payload: &mut dyn panic::PanicPayload, location: &'static Location<'static>,
) -> ! {
let panics = update_panic_count(1);
let message: &str = payload.as_str().unwrap_or_default();
rtprintpanic!(
"panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n"
);
if panics > 1 {
rtabort!("thread panicked while processing panic. aborting.");
}
rust_panic(payload)
}
#[inline]
pub fn panicking() -> bool {
update_panic_count(0) > 0
}
fn update_panic_count(amt: isize) -> usize {
static mut PANIC_COUNT: usize = 0;
unsafe {
PANIC_COUNT = (PANIC_COUNT as isize + amt) as usize;
PANIC_COUNT
}
}
#[allow(improper_ctypes)]
unsafe extern "C" {
#[cfg_attr(not(bootstrap), rustc_std_internal_symbol)]
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
#[allow(unexpected_cfgs)]
unsafe extern "Rust" {
#[cfg_attr(not(bootstrap), rustc_std_internal_symbol)]
fn __rust_start_panic(payload: &mut dyn panic::PanicPayload) -> u32;
}
#[cfg_attr(not(bootstrap), rustc_std_internal_symbol)]
extern "C" fn __rust_foreign_exception() -> ! {
loop {
core::hint::spin_loop()
}
}
#[inline(never)]
#[unsafe(no_mangle)]
#[cfg(not(feature = "std"))]
fn rust_panic(msg: &mut dyn panic::PanicPayload) -> ! {
let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {}", code)
}
#[allow(unexpected_cfgs)]
#[cfg(not(feature = "std"))]
#[cfg_attr(not(bootstrap), rustc_std_internal_symbol)]
extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown");
}
#[cfg(all(feature = "std", not(target_os = "psp")))]
pub use std::panic::catch_unwind;
#[inline(never)]
#[cfg(all(not(feature = "std"), panic = "immediate-abort"))]
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
Ok(f())
}
#[inline(never)]
#[cfg(all(not(feature = "std"), not(panic = "immediate-abort")))]
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
union Data<F, R> {
f: ManuallyDrop<F>,
r: ManuallyDrop<R>,
p: ManuallyDrop<Box<dyn Any + Send>>,
}
let mut data = Data {
f: ManuallyDrop::new(f),
};
unsafe {
return if core::intrinsics::catch_unwind(do_call, &raw mut data, do_catch) {
Err(ManuallyDrop::into_inner(data.p))
} else {
Ok(ManuallyDrop::into_inner(data.r))
};
}
#[cold]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
update_panic_count(-1);
obj
}
#[inline]
unsafe fn do_call<F: FnOnce() -> R, R>(data: *mut Data<F, R>) {
unsafe {
let f = ManuallyDrop::take(&mut (*data).f);
(*data).r = ManuallyDrop::new(f());
}
}
#[inline]
unsafe fn do_catch<F: FnOnce() -> R, R>(data: *mut Data<F, R>, payload: *mut u8) {
unsafe {
let obj = cleanup(payload);
(*data).p = ManuallyDrop::new(obj);
}
}
}
#[cfg(not(feature = "std"))]
#[lang = "eh_personality"]
unsafe extern "C" fn rust_eh_personality() {}
#[cfg_attr(panic = "unwind", link(name = "unwind", kind = "static"))]
unsafe extern "C" {}
#[cfg(all(target_os = "psp", feature = "non-stub-code", not(panic = "immediate-abort")))]
mod libunwind_shims {
#[unsafe(no_mangle)]
unsafe extern "C" fn fprintf(_stream: *const u8, _format: *const u8, _: ...) -> isize {
-1
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fflush(_stream: *const u8) -> i32 {
-1
}
#[unsafe(no_mangle)]
#[allow(deprecated)]
unsafe extern "C" fn abort() {
crate::process::abort()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
use alloc::alloc::{alloc, Layout};
let size = size + 4;
unsafe {
let data = alloc(Layout::from_size_align_unchecked(size, 4));
*(data as *mut usize) = size;
data.offset(4)
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn free(data: *mut u8) {
use alloc::alloc::{dealloc, Layout};
unsafe {
let base = data.sub(4);
let size = *(base as *mut usize);
dealloc(base, Layout::from_size_align_unchecked(size, 4));
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn getenv(_name: *const u8) -> *const u8 {
core::ptr::null()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn __assert_func(_: *const u8, _: i32, _: *const u8, _: *const u8) {}
#[unsafe(no_mangle)]
static _impure_ptr: [usize; 0] = [];
}
pub(crate) struct AssertUnwindSafe<T>(pub T);
impl<T> core::panic::UnwindSafe for AssertUnwindSafe<T> {}