mlua/mlua-sys/src/luau/lobject.rs

264 lines
6.9 KiB
Rust

use std::ffi::{c_char, c_float, c_int, c_void};
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
use crate::{lua_CFunction, lua_Continuation, lua_Integer, lua_Number, GCObject, LUA_TTABLE, LUA_TNIL, LUA_TNUMBER, LUA_TLIGHTUSERDATA, LUA_TSTRING};
#[cfg(not(feature = "luau-vector4"))]
pub const LUA_VECTOR_SIZE: usize = 3;
#[cfg(feature = "luau-vector4")]
pub const LUA_VECTOR_SIZE: usize = 4;
pub const LUA_EXTRA_SIZE: usize = LUA_VECTOR_SIZE - 2;
opaque! {
pub type LuaNode;
pub type Proto;
pub type TString;
pub type Udata;
pub type UpVal;
}
pub type StkId = *mut TValue;
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct GCheader {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub union Value {
pub gc: *mut GCObject,
/// Light user data
pub p: *mut c_void,
/// Number
pub n: lua_Number,
pub b: c_int,
/// Vector
///
/// `v[0]`, `v[1]` live here; `v[2]`, possibly `v[3]` lives in TValue::extra
pub v: [c_float; 2],
bits: u64,
}
impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:b}", unsafe { self.bits })
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct TValue {
pub value: Value,
pub extra: [c_int; LUA_EXTRA_SIZE],
pub tt: c_int,
}
impl TValue {
#[inline(always)]
fn check_type(&self, tt: c_int) {
debug_assert_eq!(self.tt, tt, "Wrong type");
}
#[inline(always)]
pub unsafe fn as_table(&self) -> *mut Table {
self.check_type(LUA_TTABLE);
unsafe { self.value.gc.cast() }
}
#[inline(always)]
pub unsafe fn as_light_user_data(&self) -> *mut c_void {
self.check_type(LUA_TLIGHTUSERDATA);
unsafe { self.value.p }
}
#[inline(always)]
pub unsafe fn as_number(&self) -> lua_Number {
self.check_type(LUA_TNUMBER);
unsafe { self.value.n }
}
/// The only check if for rounding error, not type.
#[inline(always)]
pub unsafe fn try_as_integer(&self) -> Option<c_int> {
let n = self.as_number();
let n_int = n as lua_Integer;
if (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
Some(n_int)
} else {
None
}
}
#[inline(always)]
pub unsafe fn as_integer(&self) -> lua_Integer {
self.as_number() as lua_Integer
}
#[inline(always)]
pub fn is_collectable(&self) -> bool {
self.tt >= LUA_TSTRING
}
}
#[repr(C)]
pub struct Closure {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
pub is_c: u8,
pub n_upvals: u8,
pub stack_size: u8,
pub preload: u8,
pub gc_list: *mut GCObject,
pub env: *mut Table,
pub body: ClosureBody,
}
impl Closure {
#[inline(always)]
pub fn is_c(&self) -> bool {
self.is_c != 0
}
}
impl std::fmt::Debug for Closure {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Closure")
.field("tt", &self.tt)
.field("marked", &self.marked)
.field("memcat", &self.memcat)
.field("is_c", &self.is_c)
.field("n_upvals", &self.n_upvals)
.field("stack_size", &self.stack_size)
.field("preload", &self.preload)
.field("gc_List", &self.gc_list)
.field("env", &self.env)
.field("body", if self.is_c() {
unsafe { &self.body.c }
} else {
unsafe { &self.body.l }
})
.finish()
}
}
#[repr(C)]
pub union ClosureBody {
pub c: ManuallyDrop<ClosureC>,
pub l: ManuallyDrop<ClosureL>,
}
#[repr(C)]
pub struct ClosureC {
pub f: lua_CFunction,
pub cont: lua_Continuation,
pub debugname: *const c_char,
pub upvals: [TValue; 1],
}
impl std::fmt::Debug for ClosureC {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ClosureC")
.field("f", &self.f)
.field("cont", &self.cont)
.field("debugname", unsafe { &std::ffi::CStr::from_ptr(self.debugname) })
.field("upvals", &std::ptr::addr_of!(self.upvals))
.finish()
}
}
#[repr(C)]
pub struct ClosureL {
pub p: *mut Proto,
pub uprefs: [TValue; 1],
}
impl std::fmt::Debug for ClosureL {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ClosureL")
.field("p", &self.p)
.field("uprefs", &std::ptr::addr_of!(self.uprefs))
.finish()
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Table {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
/// 1<<p means tagmethod(p) is not present
pub tmcache: u8,
/// sandboxing feature to prohibit writes to table
pub readonly: bool,
/// environment doesn't share globals with other scripts
pub safeenv: bool,
/// log2 of size of `node' array
pub l_size_node: u8,
/// (1<<lsizenode)-1, truncated to 8 bits
pub node_mask8: u8,
/// size of `array' array
pub size_array: c_int,
/// lastfree: any free position is before this position
/// aboundary: negated 'boundary' of `array' array; iff aboundary < 0
pub lastfree_or_aboundary: c_int,
pub metatable: Option<NonNull<Table>>,
/// array part
pub array: Option<NonNull<TValue>>,
/// dict part. If there is no node, the value will be the address of [`crate::luaH_dummynode`].
pub node: NonNull<LuaNode>,
pub gc_list: *mut GCObject,
}
impl Table {
#[inline(always)]
pub unsafe fn array(&self) -> &[TValue] {
debug_assert!(self.size_array >= 0);
debug_assert_eq!(self.array.is_none(), self.size_array == 0, "inconsistent array properties: null pointer with non-zero length");
match (self.array, self.size_array) {
(None, 0) => &[],
// in debug_assertions this will be caught, otherwise, allow the optimizer to ignore it
(Some(_), 0) | (None, _) => std::hint::unreachable_unchecked(),
(Some(ptr), len) => std::slice::from_raw_parts(ptr.as_ptr(), len as _)
}
}
#[inline(always)]
pub unsafe fn array_mut(&mut self) -> &mut [TValue] {
debug_assert!(self.size_array >= 0);
debug_assert_eq!(self.array.is_none(), self.size_array == 0, "inconsistent array properties: null pointer with non-zero length");
match (self.array, self.size_array) {
(None, 0) => &mut [],
// in debug_assertions this will be caught, otherwise, allow the optimizer to ignore it
(Some(_), 0) | (None, _) => std::hint::unreachable_unchecked(),
(Some(ptr), len) => std::slice::from_raw_parts_mut(ptr.as_ptr(), len as _)
}
}
#[inline(always)]
pub unsafe fn array_lsize(&self) -> usize {
self.array().iter().rposition(|v| v.tt != LUA_TNIL).map(|i| i+1).unwrap_or(0)
}
}