264 lines
6.9 KiB
Rust
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)
|
|
}
|
|
}
|