pub mod chunk_serializer;
pub mod lua_convert;
mod lua_string;
mod lua_table;
mod lua_value;
pub mod userdata_builder;
pub mod userdata_trait;
use self::lua_value::Value;
use std::any::Any;
use std::fmt;
pub use lua_string::*;
pub use userdata_builder::UserDataBuilder;
pub use userdata_trait::{
LuaEnum, LuaMethodProvider, LuaRegistrable, LuaStaticMethodProvider, OpaqueUserData, UdValue,
UserDataTrait, lua_value_to_udvalue, udvalue_to_lua_value,
};
pub use lua_table::LuaTable;
pub use lua_value::{LuaValue, LuaValueKind};
pub use lua_value::{
LUA_TBOOLEAN, LUA_TNIL, LUA_TNUMBER, LUA_TSTRING, LUA_VFALSE, LUA_VNIL, LUA_VNUMFLT,
LUA_VNUMINT, LUA_VTABLE, LUA_VTRUE,
};
use crate::lua_vm::CFunction;
use crate::{Instruction, ProtoPtr, RefUserData, TablePtr, UpvaluePtr};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct LuaValuePtr {
pub ptr: *mut LuaValue,
}
pub struct LuaUpvalue {
v: *mut LuaValue,
closed_value: LuaValue,
stack_index: usize,
}
impl LuaUpvalue {
#[inline(always)]
pub fn new_open(stack_index: usize, stack_ptr: LuaValuePtr) -> Self {
LuaUpvalue {
v: stack_ptr.ptr,
closed_value: LuaValue::nil(),
stack_index,
}
}
#[inline(always)]
pub fn new_closed(value: LuaValue) -> Self {
LuaUpvalue {
v: std::ptr::null_mut(),
closed_value: value,
stack_index: 0,
}
}
#[inline(always)]
pub fn fix_closed_ptr(&mut self) {
if self.v.is_null() {
self.v = &mut self.closed_value as *mut LuaValue;
}
}
#[inline(always)]
pub fn is_open(&self) -> bool {
!std::ptr::eq(self.v, &self.closed_value)
}
#[inline(always)]
pub fn get_stack_index(&self) -> usize {
self.stack_index
}
#[inline(always)]
pub fn close(&mut self, stack_value: LuaValue) {
self.closed_value = stack_value;
self.v = &mut self.closed_value as *mut LuaValue;
}
#[inline(always)]
pub fn update_stack_ptr(&mut self, ptr: *mut LuaValue) {
self.v = ptr;
}
#[inline(always)]
pub fn get_v_ptr(&self) -> *mut LuaValue {
self.v
}
#[inline(always)]
pub fn get_value(&self) -> LuaValue {
debug_assert!(!self.v.is_null(), "upvalue get_value: null pointer");
debug_assert!(
(self.v as usize) > 0x10000,
"upvalue get_value: suspiciously low pointer {:p} (stack_index={})",
self.v,
self.stack_index
);
let val = unsafe { *self.v };
debug_assert!(
Self::is_valid_tt(val.tt()),
"upvalue get_value: INVALID type tag 0x{:02X} read from {:p} (stack_index={}, is_open={}). Likely dangling pointer!",
val.tt(),
self.v,
self.stack_index,
self.is_open()
);
val
}
#[inline(always)]
pub fn get_value_ref(&self) -> &LuaValue {
debug_assert!(!self.v.is_null(), "upvalue get_value_ref: null pointer");
unsafe { &*self.v }
}
#[inline(always)]
pub fn set_value(&mut self, val: LuaValue) {
debug_assert!(!self.v.is_null(), "upvalue set_value: null pointer");
debug_assert!(
(self.v as usize) > 0x10000,
"upvalue set_value: suspiciously low pointer {:p} (stack_index={})",
self.v,
self.stack_index
);
unsafe { *self.v = val }
}
#[inline(always)]
pub fn set_value_parts(&mut self, value: Value, tt: u8) {
debug_assert!(!self.v.is_null(), "upvalue set_value_parts: null pointer");
debug_assert!(
(self.v as usize) > 0x10000,
"upvalue set_value_parts: suspiciously low pointer {:p} (stack_index={})",
self.v,
self.stack_index
);
unsafe {
(*self.v).value = value;
(*self.v).tt = tt;
}
}
fn is_valid_tt(tt: u8) -> bool {
use crate::lua_value::lua_value::*;
matches!(
tt,
LUA_VNIL
| LUA_VEMPTY
| LUA_VABSTKEY
| LUA_VFALSE
| LUA_VTRUE
| LUA_VNUMINT
| LUA_VNUMFLT
| LUA_VSHRSTR
| LUA_VLNGSTR
| LUA_VTABLE
| LUA_VFUNCTION
| LUA_CCLOSURE
| LUA_VLCF
| LUA_VLIGHTUSERDATA
| LUA_VUSERDATA
| LUA_VTHREAD
)
}
pub fn get_closed_value(&self) -> Option<&LuaValue> {
if !self.is_open() {
Some(&self.closed_value)
} else {
None
}
}
}
pub struct LuaUserdata {
data: Box<dyn UserDataTrait>,
metatable: TablePtr,
}
impl LuaUserdata {
pub fn new<T: UserDataTrait>(data: T) -> Self {
LuaUserdata {
data: Box::new(data),
metatable: TablePtr::null(),
}
}
pub fn from_boxed(data: Box<dyn UserDataTrait>) -> Self {
LuaUserdata {
data,
metatable: TablePtr::null(),
}
}
#[inline]
pub unsafe fn from_ref<T: UserDataTrait>(reference: &mut T) -> Self {
LuaUserdata {
data: Box::new(unsafe { RefUserData::new(reference) }),
metatable: TablePtr::null(),
}
}
#[inline]
pub unsafe fn from_raw_ptr<T: UserDataTrait>(ptr: *mut T) -> Self {
LuaUserdata {
data: Box::new(unsafe { RefUserData::from_raw(ptr) }),
metatable: TablePtr::null(),
}
}
pub fn with_metatable<T: UserDataTrait>(data: T, metatable: TablePtr) -> Self {
LuaUserdata {
data: Box::new(data),
metatable,
}
}
#[inline]
pub fn get_trait(&self) -> &dyn UserDataTrait {
self.data.as_ref()
}
#[inline]
pub fn get_trait_mut(&mut self) -> &mut dyn UserDataTrait {
self.data.as_mut()
}
#[inline]
pub fn type_name(&self) -> &'static str {
self.data.type_name()
}
#[inline]
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.data.as_any().downcast_ref::<T>()
}
#[inline]
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.data.as_any_mut().downcast_mut::<T>()
}
pub fn get_data(&self) -> &dyn Any {
self.data.as_any()
}
pub fn get_data_mut(&mut self) -> &mut dyn Any {
self.data.as_any_mut()
}
pub fn get_metatable(&self) -> Option<LuaValue> {
if self.metatable.is_null() {
None
} else {
Some(LuaValue::table(self.metatable))
}
}
pub(crate) fn set_metatable(&mut self, metatable: LuaValue) {
if let Some(table_ptr) = metatable.as_table_ptr() {
self.metatable = table_ptr;
} else if metatable.is_nil() {
self.metatable = TablePtr::null();
} else {
debug_assert!(
false,
"Attempted to set userdata metatable to non-table, non-nil value"
);
}
}
}
impl fmt::Debug for LuaUserdata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Userdata({}@{:p})",
self.data.type_name(),
self.data.as_any() as *const dyn Any
)
}
}
#[derive(Debug, Clone)]
pub struct UpvalueDesc {
pub name: String, pub is_local: bool, pub index: u32, }
#[derive(Debug, Clone)]
pub struct LocVar {
pub name: String, pub startpc: u32, pub endpc: u32, }
#[derive(Debug, Clone)]
pub struct Chunk {
pub code: Vec<Instruction>,
pub constants: Vec<LuaValue>,
pub locals: Vec<LocVar>,
pub upvalue_count: usize,
pub param_count: usize,
pub is_vararg: bool, pub needs_vararg_table: bool, pub use_hidden_vararg: bool, pub max_stack_size: usize,
pub child_protos: Vec<ProtoPtr>, pub upvalue_descs: Vec<UpvalueDesc>, pub source_name: Option<String>, pub line_info: Vec<u32>, pub linedefined: usize, pub lastlinedefined: usize, pub proto_data_size: u32, }
impl Default for Chunk {
fn default() -> Self {
Self::new()
}
}
impl Chunk {
pub fn new() -> Self {
Chunk {
code: Vec::new(),
constants: Vec::new(),
locals: Vec::new(),
upvalue_count: 0,
param_count: 0,
is_vararg: false,
needs_vararg_table: false,
use_hidden_vararg: false,
max_stack_size: 0,
child_protos: Vec::new(),
upvalue_descs: Vec::new(),
source_name: None,
line_info: Vec::new(),
linedefined: 0,
lastlinedefined: 0,
proto_data_size: 0,
}
}
pub fn compute_proto_data_size(&mut self) {
use std::mem::size_of;
let instr_size = self.code.len() * size_of::<crate::lua_vm::Instruction>();
let const_size = self.constants.len() * size_of::<LuaValue>();
let child_size = self.child_protos.len() * size_of::<ProtoPtr>();
let line_size = self.line_info.len() * size_of::<u32>();
self.proto_data_size = (instr_size + const_size + child_size + line_size) as u32;
}
#[cfg(feature = "shared-proto")]
pub fn share_constant_strings(&mut self) -> usize {
let mut shared_count = 0;
for constant in &mut self.constants {
shared_count += usize::from(crate::gc::share_lua_value(constant));
}
shared_count
}
#[cfg(feature = "shared-proto")]
pub fn share_proto_strings(&mut self) -> usize {
let mut shared_count = self.share_constant_strings();
for child in &mut self.child_protos {
shared_count += child.as_mut_ref().data.share_proto_strings();
}
shared_count
}
}
pub enum UpvalueStore {
Empty,
One(UpvaluePtr),
Many(Box<[UpvaluePtr]>),
}
impl UpvalueStore {
#[inline(always)]
pub fn from_single(ptr: UpvaluePtr) -> Self {
UpvalueStore::One(ptr)
}
#[inline(always)]
pub fn from_vec(v: Vec<UpvaluePtr>) -> Self {
match v.len() {
0 => UpvalueStore::Empty,
1 => UpvalueStore::One(v[0]),
_ => UpvalueStore::Many(v.into_boxed_slice()),
}
}
#[inline(always)]
pub fn as_slice(&self) -> &[UpvaluePtr] {
match self {
UpvalueStore::Empty => &[],
UpvalueStore::One(p) => std::slice::from_ref(p),
UpvalueStore::Many(b) => b,
}
}
#[inline(always)]
pub fn as_mut_slice(&mut self) -> &mut [UpvaluePtr] {
match self {
UpvalueStore::Empty => &mut [],
UpvalueStore::One(p) => std::slice::from_mut(p),
UpvalueStore::Many(b) => b,
}
}
#[inline(always)]
pub fn len(&self) -> usize {
match self {
UpvalueStore::Empty => 0,
UpvalueStore::One(_) => 1,
UpvalueStore::Many(b) => b.len(),
}
}
}
pub struct LuaFunction {
chunk: ProtoPtr,
upvalue_store: UpvalueStore,
}
impl LuaFunction {
pub fn new(chunk: ProtoPtr, upvalue_store: UpvalueStore) -> Self {
LuaFunction {
chunk,
upvalue_store,
}
}
#[inline(always)]
pub fn chunk(&self) -> &Chunk {
&self.chunk.as_ref().data
}
#[inline(always)]
pub fn proto(&self) -> ProtoPtr {
self.chunk
}
#[inline(always)]
pub fn upvalues(&self) -> &[UpvaluePtr] {
self.upvalue_store.as_slice()
}
#[inline(always)]
pub fn upvalues_mut(&mut self) -> &mut [UpvaluePtr] {
self.upvalue_store.as_mut_slice()
}
}
pub struct CClosureFunction {
func: CFunction,
upvalues: Vec<LuaValue>,
}
impl CClosureFunction {
pub fn new(func: CFunction, upvalues: Vec<LuaValue>) -> Self {
CClosureFunction { func, upvalues }
}
#[inline(always)]
pub fn func(&self) -> CFunction {
self.func
}
#[inline(always)]
pub fn upvalues(&self) -> &Vec<LuaValue> {
&self.upvalues
}
#[inline(always)]
pub fn upvalues_mut(&mut self) -> &mut Vec<LuaValue> {
&mut self.upvalues
}
}
pub type RustCallback = Box<dyn Fn(&mut crate::lua_vm::LuaState) -> crate::LuaResult<usize>>;
pub struct RClosureFunction {
func: RustCallback,
upvalues: Vec<LuaValue>,
}
impl RClosureFunction {
pub fn new(func: RustCallback, upvalues: Vec<LuaValue>) -> Self {
RClosureFunction { func, upvalues }
}
#[inline(always)]
pub fn call(&self, state: &mut crate::lua_vm::LuaState) -> crate::LuaResult<usize> {
(self.func)(state)
}
#[inline(always)]
pub fn upvalues(&self) -> &Vec<LuaValue> {
&self.upvalues
}
#[inline(always)]
pub fn upvalues_mut(&mut self) -> &mut Vec<LuaValue> {
&mut self.upvalues
}
}
#[cfg(test)]
mod value_tests {
use super::*;
#[test]
fn test_integer_float_distinction() {
let int_val = LuaValue::integer(42);
let float_val = LuaValue::number(42.0);
assert!(int_val.is_integer());
assert!(!int_val.is_float());
assert!(!float_val.is_integer()); assert!(float_val.is_float());
assert!(int_val.is_number());
assert!(float_val.is_number());
}
#[test]
fn test_integer_float_conversion() {
let int_val = LuaValue::integer(42);
let float_val = LuaValue::number(42.5);
assert_eq!(int_val.as_float(), Some(42.0));
assert_eq!(float_val.as_integer(), None);
let exact_float = LuaValue::number(42.0);
assert_eq!(exact_float.as_integer(), Some(42));
}
#[test]
fn test_as_number_unified() {
let int_val = LuaValue::integer(42);
let float_val = LuaValue::number(3.15);
assert_eq!(int_val.as_number(), Some(42.0));
assert_eq!(float_val.as_number(), Some(3.15));
}
}