mod coroutine;
mod protected;
mod stack;
pub(crate) use coroutine::{resume as resume_protected, resume_handle};
pub use protected::{ErrorRuntime, LuaProtectedErrorFrame, Pfunc, ProtectedCall};
pub use stack::ThreadStack;
use crate::gc::{GcBarrier, GcRuntime};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::state::ThreadState;
use crate::state::{THREAD_STATUS_BREAK, THREAD_STATUS_SCHEDULED_REENTRY, THREAD_STATUS_YIELD};
use crate::thread::{LUA_MULTRET, Thread};
use crate::value::TValueCursor;
use crate::vm::{PreCallResult, VmCallFrame, VmExecution};
use crate::{VmControl, VmExit, VmResult};
#[allow(
clippy::missing_safety_doc,
reason = "all methods share the capability-level safety contract"
)]
pub trait CallRuntime: Sealed {
unsafe fn perform_cally(&self, function: TValueCursor, n_results: i32) -> VmResult;
unsafe fn call_int(
&self,
function: TValueCursor,
n_results: i32,
prepare_reentry: bool,
) -> VmResult;
unsafe fn call_internal(&self, function: TValueCursor, n_results: i32) -> VmResult;
unsafe fn call_no_yield(&self, function: TValueCursor, n_results: i32) -> VmResult;
}
unsafe fn perform_call(
thread: &Thread,
function: TValueCursor,
n_results: i32,
prepare_reentry: bool,
) -> VmResult {
if unsafe { thread.pre_call(function, n_results)? } == PreCallResult::Lua {
unsafe {
let call_info = thread.current_call_info();
call_info.as_ptr().as_mut().unwrap_unchecked().flags |=
crate::state::LUA_CALLINFO_RETURN;
let old_active = thread.as_ptr().as_ref().unwrap_unchecked().is_active;
thread.as_ptr().as_mut().unwrap_unchecked().is_active = true;
thread.thread_barrier();
if prepare_reentry {
thread.as_ptr().as_mut().unwrap_unchecked().status =
THREAD_STATUS_SCHEDULED_REENTRY;
if !old_active {
thread.as_ptr().as_mut().unwrap_unchecked().is_active = false;
}
Err(VmExit::Control(VmControl::Yield))
} else {
let result = thread.execute();
if !old_active {
thread.as_ptr().as_mut().unwrap_unchecked().is_active = false;
}
result
}
}
} else {
Ok(())
}
}
impl CallRuntime for Thread {
unsafe fn perform_cally(&self, function: TValueCursor, n_results: i32) -> VmResult {
unsafe {
self.increment_native_call_depth();
if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
>= crate::thread::LUAI_MAX_NATIVE_CALLS
{
self.check_c_stack()?;
}
self.increment_base_native_call_depth();
let ci_offset = self.save_ci(self.current_call_info_cursor());
match perform_call(self, function, n_results, false) {
Ok(()) => {}
Err(VmExit::Control(control)) => {
let caller = self.restore_ci(ci_offset).call_info_unchecked();
caller.as_ptr().as_mut().unwrap_unchecked().flags |=
crate::state::LUA_CALLINFO_OP_YIELD;
return Err(VmExit::Control(control));
}
Err(exit) => return Err(exit),
}
self.decrement_base_native_call_depth();
self.decrement_native_call_depth();
self.check_gc()?;
Ok(())
}
}
unsafe fn call_int(
&self,
function: TValueCursor,
n_results: i32,
prepare_reentry: bool,
) -> VmResult {
let (from_yieldable_native_call, function_offset, ci_offset) = unsafe {
self.increment_native_call_depth();
if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
>= crate::thread::LUAI_MAX_NATIVE_CALLS
{
self.check_c_stack()?;
}
let mut from_yieldable_native_call = false;
if self.current_call_info() != self.base_call_info() {
let current = self.current_call_info().function_closure();
if current.is_native() && current.native_data().continuation.is_some() {
from_yieldable_native_call = true;
self.increment_base_native_call_depth();
}
}
let function_offset = self.save_stack(function);
let ci_offset = self.save_ci(self.current_call_info_cursor());
(from_yieldable_native_call, function_offset, ci_offset)
};
unsafe {
let result = perform_call(self, function, n_results, prepare_reentry);
let control = match result {
Ok(()) => None,
Err(VmExit::Control(control)) => Some(control),
Err(exit) => return Err(exit),
};
let suspended = control.is_some();
if from_yieldable_native_call {
self.decrement_base_native_call_depth();
if suspended {
let caller = self.restore_ci(ci_offset).call_info_unchecked();
let caller_top =
self.restore_stack(function_offset)
.add(if n_results != LUA_MULTRET {
n_results as usize
} else {
0
});
caller.set_top(caller_top);
}
}
if n_results != LUA_MULTRET && !suspended {
self.set_stack_top(self.restore_stack(function_offset).add(n_results as usize))
}
self.decrement_native_call_depth();
self.check_gc()?;
if let Some(control) = control {
return Err(VmExit::Control(control));
}
}
Ok(())
}
unsafe fn call_internal(&self, function: TValueCursor, n_results: i32) -> VmResult {
unsafe { self.call_int(function, n_results, false) }
}
unsafe fn call_no_yield(&self, function: TValueCursor, n_results: i32) -> VmResult {
unsafe {
self.increment_native_call_depth();
if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
>= crate::thread::LUAI_MAX_NATIVE_CALLS
{
self.check_c_stack()?;
}
debug_assert!(
self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
> self
.as_ptr()
.as_ref()
.unwrap_unchecked()
.base_native_call_depth
);
let function_offset = self.save_stack(function);
perform_call(self, function, n_results, false)?;
debug_assert!(!matches!(
self.as_ptr().as_ref().unwrap_unchecked().status,
THREAD_STATUS_YIELD | THREAD_STATUS_BREAK | THREAD_STATUS_SCHEDULED_REENTRY
));
if n_results != LUA_MULTRET {
self.set_stack_top(self.restore_stack(function_offset).add(n_results as usize))
}
self.decrement_native_call_depth();
self.check_gc()?;
}
Ok(())
}
}