use core::cell::Cell;
use core::ptr;
use luau_vm::state::LuaCallbacks;
use crate::hooks::debug::{self, DebugHandler};
use crate::hooks::interrupt::{self, InterruptHandle, InterruptHandler, InterruptMode};
use crate::lua::Lua;
pub(crate) struct CallbackManager {
interrupt_handler: Option<Box<dyn InterruptHandler>>,
interrupt_mode: InterruptMode,
interrupt_handle: InterruptHandle,
interrupt_active: Cell<bool>,
debug_handler: Option<Box<dyn DebugHandler>>,
}
impl Default for CallbackManager {
fn default() -> Self {
Self {
interrupt_handler: None,
interrupt_mode: InterruptMode::Continuous,
interrupt_handle: InterruptHandle::new(),
interrupt_active: Cell::new(false),
debug_handler: None,
}
}
}
impl Lua {
pub(in crate::hooks) fn install_callbacks(&mut self) {
let (thread, manager) = self.callback_parts();
unsafe {
manager.install(&mut *thread.callbacks());
}
}
}
impl CallbackManager {
pub(in crate::hooks) fn set_interrupt_handler(&mut self, handler: Box<dyn InterruptHandler>) {
self.interrupt_handle.clear();
self.interrupt_mode = handler.mode();
self.interrupt_handler = Some(handler);
}
pub(in crate::hooks) fn remove_interrupt_handler(&mut self) {
self.interrupt_handle.clear();
self.interrupt_mode = InterruptMode::Continuous;
self.interrupt_handler = None;
}
pub(in crate::hooks) fn set_debug_handler(&mut self, handler: Box<dyn DebugHandler>) {
self.debug_handler = Some(handler);
}
pub(in crate::hooks) fn remove_debug_handler(&mut self) {
self.debug_handler = None;
}
pub(crate) fn install(&self, callbacks: &mut LuaCallbacks) {
callbacks.execution_interrupt = self
.has_interrupt_handler()
.then_some(interrupt::execution_interrupt);
callbacks.pattern_interrupt = self
.has_interrupt_handler()
.then_some(interrupt::pattern_interrupt);
callbacks.gc_interrupt = self
.has_interrupt_handler()
.then_some(interrupt::gc_interrupt);
callbacks.interrupt_request =
if self.has_interrupt_handler() && self.interrupt_mode == InterruptMode::Requested {
self.interrupt_handle.as_ptr()
} else {
ptr::null()
};
callbacks.debug_step = self.has_debug_handler().then_some(debug::debug_step);
callbacks.debug_break = self.has_debug_handler().then_some(debug::debug_break);
callbacks.debug_interrupt = self.has_debug_handler().then_some(debug::debug_interrupt);
callbacks.debug_protected_error = self
.has_debug_handler()
.then_some(debug::debug_protected_error);
}
pub(in crate::hooks) fn invoke_interrupt<R>(
&self,
invoke: impl FnOnce(&dyn InterruptHandler, &Cell<bool>) -> R,
) -> Option<R> {
let handler = self.interrupt_handler.as_deref()?;
if self.interrupt_active.replace(true) {
if self.interrupt_mode == InterruptMode::Requested {
self.interrupt_handle.request();
}
return None;
}
struct ActiveGuard<'a>(&'a Cell<bool>);
impl Drop for ActiveGuard<'_> {
fn drop(&mut self) {
self.0.set(false);
}
}
let _active = ActiveGuard(&self.interrupt_active);
let deferred = Cell::new(false);
let result = invoke(handler, &deferred);
if self.interrupt_mode == InterruptMode::Requested && deferred.get() {
self.interrupt_handle.request();
}
Some(result)
}
pub(in crate::hooks) fn interrupt_handle(&self) -> InterruptHandle {
self.interrupt_handle.clone()
}
pub(in crate::hooks) fn debug_handler(&self) -> Option<&dyn DebugHandler> {
self.debug_handler.as_deref()
}
fn has_interrupt_handler(&self) -> bool {
self.interrupt_handler.is_some()
}
fn has_debug_handler(&self) -> bool {
self.debug_handler.is_some()
}
}