1use core::sync::atomic::{AtomicBool, Ordering};
2
3use luau_common::BStr;
4
5use crate::debug::{LuaDebugInterruptHook, LuaHook};
6use crate::function::{Closure, Proto};
7use crate::gc::{GCS_ATOMIC, GCS_PAUSE, GCS_PROPAGATE, GCS_PROPAGATE_AGAIN, GCS_SWEEP};
8use crate::handle::RawHandle;
9use crate::thread::Thread;
10use crate::{VmErrorResult, VmResult};
11
12use super::{GlobalState, LUA_EXECUTION_CALLBACK_STORAGE};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub enum InterruptKind {
16 Execution,
17 Pattern,
18 Gc(GcInterrupt),
19}
20
21#[derive(Debug, Default)]
23pub struct InterruptRequest {
24 pending: AtomicBool,
25}
26
27impl InterruptRequest {
28 pub const fn new() -> Self {
30 Self {
31 pending: AtomicBool::new(false),
32 }
33 }
34
35 pub fn request(&self) {
37 self.pending.store(true, Ordering::Release);
38 }
39
40 pub fn clear(&self) {
42 self.pending.store(false, Ordering::Relaxed);
43 }
44
45 fn take(&self) -> bool {
46 if !self.pending.load(Ordering::Relaxed) {
47 return false;
48 }
49 self.pending.swap(false, Ordering::Acquire)
50 }
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub enum GcPhase {
55 Pause,
56 Propagate,
57 PropagateAgain,
58 Atomic,
59 Sweep,
60}
61
62impl GcPhase {
63 pub(crate) fn from_state(state: u8) -> Self {
64 match state {
65 GCS_PAUSE => Self::Pause,
66 GCS_PROPAGATE => Self::Propagate,
67 GCS_PROPAGATE_AGAIN => Self::PropagateAgain,
68 GCS_ATOMIC => Self::Atomic,
69 GCS_SWEEP => Self::Sweep,
70 _ => unreachable!("unexpected gc state {state}"),
71 }
72 }
73}
74
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub enum GcInterrupt {
77 BeforeStep,
78 AfterStep { previous_phase: GcPhase },
79}
80
81pub type ExecutionInterrupt = fn(&Thread) -> VmResult;
82pub type PatternInterrupt = fn(&Thread) -> VmErrorResult;
83pub type GcInterruptCallback = fn(&Thread, GcInterrupt) -> VmErrorResult;
84pub type UserThreadCallback = fn(Option<&Thread>, &Thread);
85pub type UserAtomCallback = fn(&Thread, &BStr) -> i16;
86pub type ProtectedErrorCallback = fn(&Thread) -> ProtectedErrorAction;
87pub type AllocateCallback = fn(&Thread, usize, usize);
88pub type EmbedderMark = fn(&Thread, i32);
89pub type EmbedderGc = fn(&Thread, Option<EmbedderMark>);
90pub type ExecutionClose = unsafe fn(&Thread);
91pub type ExecutionDestroy = unsafe fn(&Thread, Proto);
92pub type ExecutionEnter = unsafe fn(&Thread, Proto) -> i32;
93pub type ExecutionDisable = unsafe fn(&Thread, Proto);
94pub type ExecutionMemorySize = unsafe fn(&Thread, Proto) -> usize;
95pub type ExecutionTypeMapping = unsafe fn(&Thread, &BStr) -> u8;
96pub type ExecutionCounterData = unsafe fn(&Thread, Proto, *mut usize) -> *mut u8;
97pub type ExecutionInlineFunction = unsafe fn(&Thread, Closure, Closure, u32) -> Option<Proto>;
98
99#[derive(Clone, Copy, Default)]
100#[repr(C)]
101pub struct LuaCallbacks {
110 pub userdata: *mut (),
111 pub execution_interrupt: Option<ExecutionInterrupt>,
112 pub pattern_interrupt: Option<PatternInterrupt>,
113 pub gc_interrupt: Option<GcInterruptCallback>,
114 pub interrupt_request: *const InterruptRequest,
115 pub user_thread: Option<UserThreadCallback>,
116 pub user_atom: Option<UserAtomCallback>,
117 pub debug_break: Option<LuaHook>,
118 pub debug_step: Option<LuaHook>,
119 pub debug_interrupt: Option<LuaDebugInterruptHook>,
120 pub debug_protected_error: Option<ProtectedErrorCallback>,
121 pub on_allocate: Option<AllocateCallback>,
122}
123
124#[derive(Clone, Copy, Default)]
125#[repr(C)]
126pub struct LuaExecutionCallbacks {
132 pub context: *mut (),
133 pub close: Option<ExecutionClose>,
134 pub destroy: Option<ExecutionDestroy>,
135 pub enter: Option<ExecutionEnter>,
136 pub disable: Option<ExecutionDisable>,
137 pub get_memory_size: Option<ExecutionMemorySize>,
138 pub get_type_mapping: Option<ExecutionTypeMapping>,
139 pub get_counter_data: Option<ExecutionCounterData>,
140 pub inline_function: Option<ExecutionInlineFunction>,
141}
142
143#[repr(C, align(16))]
144pub struct ExecutionCallbackStorage {
145 pub bytes: [u8; LUA_EXECUTION_CALLBACK_STORAGE],
146}
147
148impl GlobalState {
149 pub fn callbacks(&self) -> *mut LuaCallbacks {
155 unsafe { &raw mut (*self.as_ptr()).cb }
156 }
157
158 pub fn execution_callbacks(&self) -> *mut LuaExecutionCallbacks {
164 unsafe { &raw mut (*self.as_ptr()).ecb }
165 }
166
167 pub(crate) fn user_thread_callback(&self) -> Option<UserThreadCallback> {
168 unsafe { (*self.callbacks()).user_thread }
169 }
170
171 pub(crate) fn user_atom_callback(&self) -> Option<UserAtomCallback> {
172 unsafe { (*self.callbacks()).user_atom }
173 }
174
175 pub(crate) fn debug_break_callback(&self) -> Option<LuaHook> {
176 unsafe { (*self.callbacks()).debug_break }
177 }
178
179 pub(crate) fn debug_step_callback(&self) -> Option<LuaHook> {
180 unsafe { (*self.callbacks()).debug_step }
181 }
182
183 pub(crate) fn debug_interrupt_callback(&self) -> Option<LuaDebugInterruptHook> {
184 unsafe { (*self.callbacks()).debug_interrupt }
185 }
186
187 pub(crate) fn protected_error_callback(&self) -> Option<ProtectedErrorCallback> {
188 unsafe { (*self.callbacks()).debug_protected_error }
189 }
190
191 pub(crate) fn take_execution_interrupt_callback(&self) -> Option<ExecutionInterrupt> {
192 let callback = unsafe { (*self.callbacks()).execution_interrupt };
193 callback.filter(|_| self.should_call_interrupt())
194 }
195
196 pub(crate) fn take_pattern_interrupt_callback(&self) -> Option<PatternInterrupt> {
197 let callback = unsafe { (*self.callbacks()).pattern_interrupt };
198 callback.filter(|_| self.should_call_interrupt())
199 }
200
201 pub(crate) fn take_gc_interrupt_callback(&self) -> Option<GcInterruptCallback> {
202 let callback = unsafe { (*self.callbacks()).gc_interrupt };
203 callback.filter(|_| self.should_call_interrupt())
204 }
205
206 fn should_call_interrupt(&self) -> bool {
207 let request = unsafe { (*self.callbacks()).interrupt_request };
208 request.is_null() || unsafe { (*request).take() }
209 }
210
211 pub(crate) fn execution_close(&self) -> Option<ExecutionClose> {
212 unsafe { (*self.execution_callbacks()).close }
213 }
214
215 pub(crate) fn execution_destroy(&self) -> Option<ExecutionDestroy> {
216 unsafe { (*self.execution_callbacks()).destroy }
217 }
218
219 pub(crate) fn execution_enter(&self) -> Option<ExecutionEnter> {
220 unsafe { (*self.execution_callbacks()).enter }
221 }
222
223 pub(crate) fn execution_disable(&self) -> Option<ExecutionDisable> {
224 unsafe { (*self.execution_callbacks()).disable }
225 }
226
227 pub(crate) fn execution_memory_size(&self) -> Option<ExecutionMemorySize> {
228 unsafe { (*self.execution_callbacks()).get_memory_size }
229 }
230
231 pub(crate) fn execution_type_mapping(&self) -> Option<ExecutionTypeMapping> {
232 unsafe { (*self.execution_callbacks()).get_type_mapping }
233 }
234
235 pub(crate) fn execution_counter_data(&self) -> Option<ExecutionCounterData> {
236 unsafe { (*self.execution_callbacks()).get_counter_data }
237 }
238
239 pub(crate) fn execution_inline_function(&self) -> Option<ExecutionInlineFunction> {
240 unsafe { (*self.execution_callbacks()).inline_function }
241 }
242}
243
244#[derive(Clone, Copy, Debug, Eq, PartialEq)]
245pub enum ProtectedErrorAction {
246 Continue,
247 Break,
248}