use core::sync::atomic::{AtomicBool, Ordering};
use luau_common::BStr;
use crate::debug::{LuaDebugInterruptHook, LuaHook};
use crate::function::{Closure, Proto};
use crate::gc::{GCS_ATOMIC, GCS_PAUSE, GCS_PROPAGATE, GCS_PROPAGATE_AGAIN, GCS_SWEEP};
use crate::handle::RawHandle;
use crate::thread::Thread;
use crate::{VmErrorResult, VmResult};
use super::{GlobalState, LUA_EXECUTION_CALLBACK_STORAGE};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InterruptKind {
Execution,
Pattern,
Gc(GcInterrupt),
}
#[derive(Debug, Default)]
pub struct InterruptRequest {
pending: AtomicBool,
}
impl InterruptRequest {
pub const fn new() -> Self {
Self {
pending: AtomicBool::new(false),
}
}
pub fn request(&self) {
self.pending.store(true, Ordering::Release);
}
pub fn clear(&self) {
self.pending.store(false, Ordering::Relaxed);
}
fn take(&self) -> bool {
if !self.pending.load(Ordering::Relaxed) {
return false;
}
self.pending.swap(false, Ordering::Acquire)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GcPhase {
Pause,
Propagate,
PropagateAgain,
Atomic,
Sweep,
}
impl GcPhase {
pub(crate) fn from_state(state: u8) -> Self {
match state {
GCS_PAUSE => Self::Pause,
GCS_PROPAGATE => Self::Propagate,
GCS_PROPAGATE_AGAIN => Self::PropagateAgain,
GCS_ATOMIC => Self::Atomic,
GCS_SWEEP => Self::Sweep,
_ => unreachable!("unexpected gc state {state}"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GcInterrupt {
BeforeStep,
AfterStep { previous_phase: GcPhase },
}
pub type ExecutionInterrupt = fn(&Thread) -> VmResult;
pub type PatternInterrupt = fn(&Thread) -> VmErrorResult;
pub type GcInterruptCallback = fn(&Thread, GcInterrupt) -> VmErrorResult;
pub type UserThreadCallback = fn(Option<&Thread>, &Thread);
pub type UserAtomCallback = fn(&Thread, &BStr) -> i16;
pub type ProtectedErrorCallback = fn(&Thread) -> ProtectedErrorAction;
pub type AllocateCallback = fn(&Thread, usize, usize);
pub type EmbedderMark = fn(&Thread, i32);
pub type EmbedderGc = fn(&Thread, Option<EmbedderMark>);
pub type ExecutionClose = unsafe fn(&Thread);
pub type ExecutionDestroy = unsafe fn(&Thread, Proto);
pub type ExecutionEnter = unsafe fn(&Thread, Proto) -> i32;
pub type ExecutionDisable = unsafe fn(&Thread, Proto);
pub type ExecutionMemorySize = unsafe fn(&Thread, Proto) -> usize;
pub type ExecutionTypeMapping = unsafe fn(&Thread, &BStr) -> u8;
pub type ExecutionCounterData = unsafe fn(&Thread, Proto, *mut usize) -> *mut u8;
pub type ExecutionInlineFunction = unsafe fn(&Thread, Closure, Closure, u32) -> Option<Proto>;
#[derive(Clone, Copy, Default)]
#[repr(C)]
pub struct LuaCallbacks {
pub userdata: *mut (),
pub execution_interrupt: Option<ExecutionInterrupt>,
pub pattern_interrupt: Option<PatternInterrupt>,
pub gc_interrupt: Option<GcInterruptCallback>,
pub interrupt_request: *const InterruptRequest,
pub user_thread: Option<UserThreadCallback>,
pub user_atom: Option<UserAtomCallback>,
pub debug_break: Option<LuaHook>,
pub debug_step: Option<LuaHook>,
pub debug_interrupt: Option<LuaDebugInterruptHook>,
pub debug_protected_error: Option<ProtectedErrorCallback>,
pub on_allocate: Option<AllocateCallback>,
}
#[derive(Clone, Copy, Default)]
#[repr(C)]
pub struct LuaExecutionCallbacks {
pub context: *mut (),
pub close: Option<ExecutionClose>,
pub destroy: Option<ExecutionDestroy>,
pub enter: Option<ExecutionEnter>,
pub disable: Option<ExecutionDisable>,
pub get_memory_size: Option<ExecutionMemorySize>,
pub get_type_mapping: Option<ExecutionTypeMapping>,
pub get_counter_data: Option<ExecutionCounterData>,
pub inline_function: Option<ExecutionInlineFunction>,
}
#[repr(C, align(16))]
pub struct ExecutionCallbackStorage {
pub bytes: [u8; LUA_EXECUTION_CALLBACK_STORAGE],
}
impl GlobalState {
pub fn callbacks(&self) -> *mut LuaCallbacks {
unsafe { &raw mut (*self.as_ptr()).cb }
}
pub fn execution_callbacks(&self) -> *mut LuaExecutionCallbacks {
unsafe { &raw mut (*self.as_ptr()).ecb }
}
pub(crate) fn user_thread_callback(&self) -> Option<UserThreadCallback> {
unsafe { (*self.callbacks()).user_thread }
}
pub(crate) fn user_atom_callback(&self) -> Option<UserAtomCallback> {
unsafe { (*self.callbacks()).user_atom }
}
pub(crate) fn debug_break_callback(&self) -> Option<LuaHook> {
unsafe { (*self.callbacks()).debug_break }
}
pub(crate) fn debug_step_callback(&self) -> Option<LuaHook> {
unsafe { (*self.callbacks()).debug_step }
}
pub(crate) fn debug_interrupt_callback(&self) -> Option<LuaDebugInterruptHook> {
unsafe { (*self.callbacks()).debug_interrupt }
}
pub(crate) fn protected_error_callback(&self) -> Option<ProtectedErrorCallback> {
unsafe { (*self.callbacks()).debug_protected_error }
}
pub(crate) fn take_execution_interrupt_callback(&self) -> Option<ExecutionInterrupt> {
let callback = unsafe { (*self.callbacks()).execution_interrupt };
callback.filter(|_| self.should_call_interrupt())
}
pub(crate) fn take_pattern_interrupt_callback(&self) -> Option<PatternInterrupt> {
let callback = unsafe { (*self.callbacks()).pattern_interrupt };
callback.filter(|_| self.should_call_interrupt())
}
pub(crate) fn take_gc_interrupt_callback(&self) -> Option<GcInterruptCallback> {
let callback = unsafe { (*self.callbacks()).gc_interrupt };
callback.filter(|_| self.should_call_interrupt())
}
fn should_call_interrupt(&self) -> bool {
let request = unsafe { (*self.callbacks()).interrupt_request };
request.is_null() || unsafe { (*request).take() }
}
pub(crate) fn execution_close(&self) -> Option<ExecutionClose> {
unsafe { (*self.execution_callbacks()).close }
}
pub(crate) fn execution_destroy(&self) -> Option<ExecutionDestroy> {
unsafe { (*self.execution_callbacks()).destroy }
}
pub(crate) fn execution_enter(&self) -> Option<ExecutionEnter> {
unsafe { (*self.execution_callbacks()).enter }
}
pub(crate) fn execution_disable(&self) -> Option<ExecutionDisable> {
unsafe { (*self.execution_callbacks()).disable }
}
pub(crate) fn execution_memory_size(&self) -> Option<ExecutionMemorySize> {
unsafe { (*self.execution_callbacks()).get_memory_size }
}
pub(crate) fn execution_type_mapping(&self) -> Option<ExecutionTypeMapping> {
unsafe { (*self.execution_callbacks()).get_type_mapping }
}
pub(crate) fn execution_counter_data(&self) -> Option<ExecutionCounterData> {
unsafe { (*self.execution_callbacks()).get_counter_data }
}
pub(crate) fn execution_inline_function(&self) -> Option<ExecutionInlineFunction> {
unsafe { (*self.execution_callbacks()).inline_function }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProtectedErrorAction {
Continue,
Break,
}