use crate::platform::dynlib::DynLib;
use crate::vm::state::LuaState;
use crate::vm::value::Val;
pub const RILUA_VERSION_NUM: u32 = {
let bytes = env!("CARGO_PKG_VERSION").as_bytes();
let mut major: u32 = 0;
let mut minor: u32 = 0;
let mut patch: u32 = 0;
let mut part = 0u8; let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b == b'.' {
part += 1;
} else {
let digit = (b - b'0') as u32;
match part {
0 => major = major * 10 + digit,
1 => minor = minor * 10 + digit,
_ => patch = patch * 10 + digit,
}
}
i += 1;
}
major * 10000 + minor * 100 + patch
};
#[repr(C)]
pub struct RiluaModuleInfo {
pub magic: [u8; 6],
pub rilua_version: u32,
pub state_size: u32,
pub val_size: u32,
}
pub const RILUA_MODULE_MAGIC: [u8; 6] = *b"rilua\0";
pub type RiluaModuleEntry = unsafe extern "C" fn(state: *mut LuaState) -> i32;
#[macro_export]
macro_rules! export_module_info {
() => {
#[unsafe(no_mangle)]
pub static RILUA_MODULE_INFO: $crate::dynmod::RiluaModuleInfo =
$crate::dynmod::RiluaModuleInfo {
magic: *b"rilua\0",
rilua_version: $crate::dynmod::RILUA_VERSION_NUM,
state_size: std::mem::size_of::<$crate::vm::state::LuaState>() as u32,
val_size: std::mem::size_of::<$crate::vm::value::Val>() as u32,
};
};
}
#[allow(unsafe_code)]
pub(crate) fn validate_module_info(lib: &DynLib) -> Result<(), String> {
let ptr = lib.symbol("RILUA_MODULE_INFO")?;
let info = unsafe { &*(ptr as *const RiluaModuleInfo) };
if info.magic != RILUA_MODULE_MAGIC {
return Err(format!(
"'{}' is not a rilua module (bad magic)",
lib.path()
));
}
if info.rilua_version != RILUA_VERSION_NUM {
return Err(format!(
"module '{}' was compiled for rilua version {} but host is version {}",
lib.path(),
info.rilua_version,
RILUA_VERSION_NUM
));
}
let host_state_size = std::mem::size_of::<LuaState>() as u32;
if info.state_size != host_state_size {
return Err(format!(
"module '{}' ABI mismatch: LuaState size {} vs host {}",
lib.path(),
info.state_size,
host_state_size
));
}
let host_val_size = std::mem::size_of::<Val>() as u32;
if info.val_size != host_val_size {
return Err(format!(
"module '{}' ABI mismatch: Val size {} vs host {}",
lib.path(),
info.val_size,
host_val_size
));
}
Ok(())
}
#[allow(unsafe_code)]
pub(crate) fn load_entry_point(
lib: &DynLib,
symbol_name: &str,
) -> Result<RiluaModuleEntry, String> {
let ptr = lib.symbol(symbol_name)?;
if ptr.is_null() {
return Err(format!(
"symbol '{}' is null in '{}'",
symbol_name,
lib.path()
));
}
let func: RiluaModuleEntry = unsafe { std::mem::transmute(ptr) };
Ok(func)
}
#[allow(unsafe_code)]
pub(crate) fn call_entry_point(
entry: RiluaModuleEntry,
state: &mut LuaState,
) -> Result<i32, String> {
let state_ptr: *mut LuaState = state;
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe { entry(state_ptr) })) {
Ok(n) => {
if n < 0 {
Err(format!("module entry point returned error code {n}"))
} else {
Ok(n)
}
}
Err(_) => Err("module entry point panicked".to_string()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_num_parses_correctly() {
let version = env!("CARGO_PKG_VERSION");
let parts: Vec<u32> = version.split('.').map(|s| s.parse().unwrap_or(0)).collect();
let expected = parts[0] * 10000 + parts[1] * 100 + parts[2];
assert_eq!(RILUA_VERSION_NUM, expected);
}
#[test]
fn module_magic_is_correct() {
assert_eq!(&RILUA_MODULE_MAGIC, b"rilua\0");
}
}