diff --git a/ctr-std/src/lib.rs b/ctr-std/src/lib.rs index 146e23c..d5d4f49 100644 --- a/ctr-std/src/lib.rs +++ b/ctr-std/src/lib.rs @@ -289,6 +289,7 @@ #![feature(on_unimplemented)] #![feature(oom)] #![feature(optin_builtin_traits)] +#![feature(panic_internals)] #![feature(panic_unwind)] #![feature(peek)] #![feature(placement_in_syntax)] @@ -305,6 +306,7 @@ #![feature(slice_internals)] #![feature(slice_patterns)] #![feature(staged_api)] +#![feature(std_internals)] #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_char)] diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs index cbc375f..ad0a337 100644 --- a/ctr-std/src/panicking.rs +++ b/ctr-std/src/panicking.rs @@ -17,18 +17,19 @@ //! * Executing a panic up to doing the actual implementation //! * Shims around "try" -#![allow(unused_imports)] +use core::panic::BoxMeUp; use io::prelude::*; use any::Any; use cell::RefCell; +use core::panic::{PanicInfo, Location}; use fmt; use intrinsics; use mem; use ptr; use raw; -use sys::stdio::Stderr; +use sys::stdio::{Stderr, stderr_prints_nothing}; use sys_common::rwlock::RWLock; use sys_common::thread_info; use sys_common::util; @@ -57,7 +58,7 @@ extern { data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32; #[unwind(allowed)] - fn __rust_start_panic(data: usize, vtable: usize) -> u32; + fn __rust_start_panic(payload: usize) -> u32; } #[derive(Copy, Clone)] @@ -160,182 +161,6 @@ pub fn take_hook() -> Box { } } -/// A struct providing information about a panic. -/// -/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] -/// function. -/// -/// [`set_hook`]: ../../std/panic/fn.set_hook.html -/// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// println!("panic occurred: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); -/// })); -/// -/// panic!("Normal panic"); -/// ``` -#[stable(feature = "panic_hooks", since = "1.10.0")] -#[derive(Debug)] -pub struct PanicInfo<'a> { - payload: &'a (Any + Send), - location: Location<'a>, -} - -impl<'a> PanicInfo<'a> { - /// Returns the payload associated with the panic. - /// - /// This will commonly, but not always, be a `&'static str` or [`String`]. - /// - /// [`String`]: ../../std/string/struct.String.html - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// println!("panic occurred: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn payload(&self) -> &(Any + Send) { - self.payload - } - - /// Returns information about the location from which the panic originated, - /// if available. - /// - /// This method will currently always return [`Some`], but this may change - /// in future versions. - /// - /// [`Some`]: ../../std/option/enum.Option.html#variant.Some - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}' at line {}", location.file(), - /// location.line()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location> { - Some(&self.location) - } -} - -/// A struct containing information about the location of a panic. -/// -/// This structure is created by the [`location`] method of [`PanicInfo`]. -/// -/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location -/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html -/// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// if let Some(location) = panic_info.location() { -/// println!("panic occurred in file '{}' at line {}", location.file(), location.line()); -/// } else { -/// println!("panic occurred but can't get location information..."); -/// } -/// })); -/// -/// panic!("Normal panic"); -/// ``` -#[derive(Debug)] -#[stable(feature = "panic_hooks", since = "1.10.0")] -pub struct Location<'a> { - file: &'a str, - line: u32, - col: u32, -} - -impl<'a> Location<'a> { - /// Returns the name of the source file from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred in file '{}'", location.file()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn file(&self) -> &str { - self.file - } - - /// Returns the line number from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred at line {}", location.line()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn line(&self) -> u32 { - self.line - } - - /// Returns the column from which the panic originated. - /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(location) = panic_info.location() { - /// println!("panic occurred at column {}", location.column()); - /// } else { - /// println!("panic occurred but can't get location information..."); - /// } - /// })); - /// - /// panic!("Normal panic"); - /// ``` - #[stable(feature = "panic_col", since = "1.25")] - pub fn column(&self) -> u32 { - self.col - } -} - fn default_hook(info: &PanicInfo) { #[cfg(feature = "backtrace")] use sys_common::backtrace; @@ -353,18 +178,16 @@ fn default_hook(info: &PanicInfo) { } }; - let file = info.location.file; - let line = info.location.line; - let col = info.location.col; + let location = info.location().unwrap(); // The current implementation always returns Some - let msg = match info.payload.downcast_ref::<&'static str>() { + let msg = match info.payload().downcast_ref::<&'static str>() { Some(s) => *s, - None => match info.payload.downcast_ref::() { + None => match info.payload().downcast_ref::() { Some(s) => &s[..], None => "Box", } }; - + let mut err = Stderr::new().ok(); let thread = thread_info::current_thread(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); @@ -372,8 +195,8 @@ fn default_hook(info: &PanicInfo) { use libctru::{errorInit, errorText, errorDisp, errorConf, ERROR_TEXT_WORD_WRAP, CFG_LANGUAGE_EN, consoleDebugInit, debugDevice_SVC}; - let error_text = format!("thread '{}' panicked at '{}', {}:{}:{}", - name, msg, file, line, col); + let error_text = format!("thread '{}' panicked at '{}', {}", name, msg, location); + unsafe { // Prepare error message for display let mut error_conf: errorConf = mem::uninitialized(); @@ -393,7 +216,6 @@ fn default_hook(info: &PanicInfo) { consoleDebugInit(debugDevice_SVC); } - let mut err = Stderr::new().ok(); let write = |err: &mut ::io::Write| { let _ = write!(err, "{}", error_text); @@ -413,20 +235,19 @@ fn default_hook(info: &PanicInfo) { let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); match (prev, err.as_mut()) { - (Some(mut stderr), _) => { - write(&mut *stderr); - let mut s = Some(stderr); - LOCAL_STDERR.with(|slot| { - *slot.borrow_mut() = s.take(); - }); - } - (None, Some(ref mut err)) => { write(err) } - _ => {} + (Some(mut stderr), _) => { + write(&mut *stderr); + let mut s = Some(stderr); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); + } + (None, Some(ref mut err)) => { write(err) } + _ => {} } - // TODO: If we ever implement backtrace functionality, determine the best way to - // incorporate it with the error applet } + #[cfg(not(test))] #[doc(hidden)] #[unstable(feature = "update_panic_count", issue = "0")] @@ -544,9 +365,38 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments, // panic + OOM properly anyway (see comment in begin_panic // below). - let mut s = String::new(); - let _ = s.write_fmt(*msg); - begin_panic(s, file_line_col) + rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col); + + struct PanicPayload<'a> { + inner: &'a fmt::Arguments<'a>, + string: Option, + } + + impl<'a> PanicPayload<'a> { + fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> { + PanicPayload { inner, string: None } + } + + fn fill(&mut self) -> &mut String { + let inner = self.inner; + self.string.get_or_insert_with(|| { + let mut s = String::new(); + drop(s.write_fmt(*inner)); + s + }) + } + } + + unsafe impl<'a> BoxMeUp for PanicPayload<'a> { + fn box_me_up(&mut self) -> *mut (Any + Send) { + let contents = mem::replace(self.fill(), String::new()); + Box::into_raw(Box::new(contents)) + } + + fn get(&mut self) -> &(Any + Send) { + self.fill() + } + } } /// This is the entry point of panicking for panic!() and assert!(). @@ -562,18 +412,43 @@ pub fn begin_panic(msg: M, file_line_col: &(&'static str, u32, u3 // be performed in the parent of this thread instead of the thread that's // panicking. - rust_panic_with_hook(Box::new(msg), file_line_col) + rust_panic_with_hook(&mut PanicPayload::new(msg), None, file_line_col); + + struct PanicPayload { + inner: Option, + } + + impl PanicPayload { + fn new(inner: A) -> PanicPayload { + PanicPayload { inner: Some(inner) } + } + } + + unsafe impl BoxMeUp for PanicPayload { + fn box_me_up(&mut self) -> *mut (Any + Send) { + let data = match self.inner.take() { + Some(a) => Box::new(a) as Box, + None => Box::new(()), + }; + Box::into_raw(data) + } + + fn get(&mut self) -> &(Any + Send) { + match self.inner { + Some(ref a) => a, + None => &(), + } + } + } } -/// Executes the primary logic for a panic, including checking for recursive -/// panics and panic hooks. +/// Central point for dispatching panics. /// -/// This is the entry point or panics from libcore, formatted panics, and -/// `Box` panics. Here we'll verify that we're not panicking recursively, -/// run panic hooks, and then delegate to the actual implementation of panics. -#[inline(never)] -#[cold] -fn rust_panic_with_hook(msg: Box, +/// Executes the primary logic for a panic, including checking for recursive +/// panics, panic hooks, and finally dispatching to the panic runtime to either +/// abort or unwind. +fn rust_panic_with_hook(payload: &mut BoxMeUp, + message: Option<&fmt::Arguments>, file_line_col: &(&'static str, u32, u32)) -> ! { let (file, line, col) = *file_line_col; @@ -591,18 +466,24 @@ fn rust_panic_with_hook(msg: Box, } unsafe { - let info = PanicInfo { - payload: &*msg, - location: Location { - file, - line, - col, - }, - }; + let mut info = PanicInfo::internal_constructor( + message, + Location::internal_constructor(file, line, col), + ); HOOK_LOCK.read(); match HOOK { - Hook::Default => default_hook(&info), - Hook::Custom(ptr) => (*ptr)(&info), + // Some platforms know that printing to stderr won't ever actually + // print anything, and if that's the case we can skip the default + // hook. + Hook::Default if stderr_prints_nothing() => {} + Hook::Default => { + info.set_payload(payload.get()); + default_hook(&info); + } + Hook::Custom(ptr) => { + info.set_payload(payload.get()); + (*ptr)(&info); + } } HOOK_LOCK.read_unlock(); } @@ -617,22 +498,35 @@ fn rust_panic_with_hook(msg: Box, unsafe { intrinsics::abort() } } - rust_panic(msg) + rust_panic(payload) } /// Shim around rust_panic. Called by resume_unwind. pub fn update_count_then_panic(msg: Box) -> ! { update_panic_count(1); - rust_panic(msg) + + struct RewrapBox(Box); + + unsafe impl BoxMeUp for RewrapBox { + fn box_me_up(&mut self) -> *mut (Any + Send) { + Box::into_raw(mem::replace(&mut self.0, Box::new(()))) + } + + fn get(&mut self) -> &(Any + Send) { + &*self.0 + } + } + + rust_panic(&mut RewrapBox(msg)) } /// A private no-mangle function on which to slap yer breakpoints. #[no_mangle] #[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints -pub fn rust_panic(msg: Box) -> ! { +pub fn rust_panic(mut msg: &mut BoxMeUp) -> ! { let code = unsafe { - let obj = mem::transmute::<_, raw::TraitObject>(msg); - __rust_start_panic(obj.data as usize, obj.vtable as usize) + let obj = &mut msg as *mut &mut BoxMeUp; + __rust_start_panic(obj as usize) }; rtabort!("failed to initiate panic, error {}", code) } diff --git a/ctr-std/src/sys/unix/stdio.rs b/ctr-std/src/sys/unix/stdio.rs index e9b3d4a..87ba2ae 100644 --- a/ctr-std/src/sys/unix/stdio.rs +++ b/ctr-std/src/sys/unix/stdio.rs @@ -75,3 +75,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { } pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn stderr_prints_nothing() -> bool { + false +}