1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//! Process API. Abort and abort with error message.

/// Executes the undefined instruction (UDF) and causes a CPU-level exception.
/// See [`core::intrinsics::abort()`].
#[track_caller]
#[inline(always)]
pub fn abort() -> ! { core::intrinsics::abort() }


/// Stops the program execution with custom system-level error.
///
/// In case of missed [`crate::sys::API`] (doesn't set) uses [`abort`].
#[track_caller]
pub fn error<S: AsRef<str>>(text: S) -> ! {
	#[cfg(not(playdate))]
	{
		use core::sync::atomic::AtomicBool;
		use core::sync::atomic::Ordering;

		// This is unreachable on the device,
		// `API.system.error` interrupts the execution.
		// But simulator doesn't stops.
		// So, instead of spin-loop just save the flag and use later in the event-handler.

		#[no_mangle]
		static END_WITH_ERR: AtomicBool = AtomicBool::new(false);

		END_WITH_ERR.store(true, Ordering::Relaxed);
	}


	if let Some(f) = unsafe { (*(*crate::sys::API).system).error } {
		// Casting fn->void to fn->!
		// This ugly cast is safe because of `!` is a magic compile-time marker, not a type.
		let f: unsafe extern "C" fn(*const i8, ...) = f;
		let p = core::ptr::addr_of!(f) as *const _ as *const unsafe extern "C" fn(*const i8, ...) -> !;
		let f = unsafe { p.as_ref() }.unwrap();

		if let Ok(s) = alloc::ffi::CString::new(text.as_ref()) {
			unsafe { f(s.as_ptr() as *mut core::ffi::c_char) }
		} else {
			unsafe { f(text.as_ref().as_ptr() as *mut core::ffi::c_char) }
		}
	} else {
		// In case of `crate::sys::API` is missed (doesn't set) just abort the process.
		abort()
	}
}