pub mod alive_ref;
pub mod chunk_serializer;
pub mod lua_convert;
mod lua_string;
mod lua_table;
#[allow(clippy::module_inception)]
mod lua_value;
mod userdata;
pub mod userdata_builder;
pub mod userdata_trait;
use self::lua_value::Value;
use std::sync::Arc;
pub use lua_string::*;
pub use userdata::LuaUserdata;
pub use userdata_builder::UserDataBuilder;
pub use userdata_trait::{
lua_value_to_udvalue, udvalue_to_lua_value, udvalue_to_lua_value_with_token,
};
pub use lua_table::LuaRawTable;
pub use lua_value::{BIT_ISCOLLECTABLE, LUA_VFALSE, LUA_VNIL, LUA_VNUMFLT, LUA_VNUMINT, LUA_VTRUE};
pub use lua_value::{LuaValue, LuaValueKind};
pub(crate) type LuaInnerValue = Value;
use crate::Instruction;
use crate::gc::{ProtoPtr, UpvaluePtr};
use crate::lua_vm::{CFunction, StkId};
pub struct LuaUpvalue {
v: StkId,
closed_value: LuaValue,
stack_index: usize,
}
impl LuaUpvalue {
#[inline(always)]
pub fn new_open(stack_index: usize, stk_id: StkId) -> Self {
LuaUpvalue {
v: stk_id,
closed_value: LuaValue::nil(),
stack_index,
}
}
#[inline(always)]
pub fn new_closed(value: LuaValue) -> Self {
LuaUpvalue {
v: StkId::null(),
closed_value: value,
stack_index: 0,
}
}
#[inline(always)]
pub fn fix_closed_ptr(&mut self) {
if !self.v.is_valid() {
self.v = StkId::from_mut_ptr(&mut self.closed_value as *mut LuaValue);
}
}
#[inline(always)]
pub fn is_open(&self) -> bool {
!std::ptr::eq(self.v.as_ptr(), &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 = StkId::from_mut_ptr(&mut self.closed_value as *mut LuaValue);
}
#[inline(always)]
pub fn update_stack_ptr(&mut self, stk_id: StkId) {
self.v = stk_id;
}
#[inline(always)]
pub fn get_v_stk_id(&self) -> StkId {
self.v
}
#[inline(always)]
pub fn get_value(&self) -> LuaValue {
self.v.get()
}
#[inline(always)]
pub fn get_value_ref(&self) -> &LuaValue {
self.v.get_ref()
}
#[inline(always)]
pub fn set_value(&mut self, val: LuaValue) {
self.v.write(&val);
}
#[inline(always)]
pub fn set_value_parts(&mut self, value: Value, tt: u8) {
self.v.write_parts(tt, value);
}
pub fn get_closed_value(&self) -> Option<&LuaValue> {
if !self.is_open() {
Some(&self.closed_value)
} else {
None
}
}
}
#[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 LuaProto {
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<Arc<str>>, pub line_info: Vec<u32>, pub linedefined: usize, pub lastlinedefined: usize, pub proto_data_size: u32, }
impl Default for LuaProto {
fn default() -> Self {
Self::new()
}
}
impl LuaProto {
pub fn new() -> Self {
LuaProto {
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 LuaRawFunction {
chunk: ProtoPtr,
upvalue_store: UpvalueStore,
}
impl LuaRawFunction {
pub fn new(chunk: ProtoPtr, upvalue_store: UpvalueStore) -> Self {
LuaRawFunction {
chunk,
upvalue_store,
}
}
#[inline(always)]
pub fn chunk(&self) -> &LuaProto {
&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));
}
}