use crate::runtime::heap::{Gc, GcHeader, Marker};
use crate::runtime::string::LuaStr;
use crate::runtime::value::Value;
use crate::vm::isa::Inst;
#[derive(Clone, Copy)]
pub struct Frame {
pub closure: Gc<LuaClosure>,
pub base: u32,
pub pc: u32,
pub func_slot: u32,
pub n_varargs: u32,
pub nresults: i32,
pub hook_oldpc: u32,
pub from_c: bool,
pub tm: Option<&'static str>,
pub is_hook: bool,
pub tailcalls: u32,
}
#[derive(Clone, Copy)]
pub enum CallFrame {
Lua(
Frame,
),
Cont(
NativeCont,
),
}
impl CallFrame {
#[inline]
pub fn lua(&self) -> Option<&Frame> {
match self {
CallFrame::Lua(f) => Some(f),
CallFrame::Cont(_) => None,
}
}
#[inline]
pub fn lua_mut(&mut self) -> Option<&mut Frame> {
match self {
CallFrame::Lua(f) => Some(f),
CallFrame::Cont(_) => None,
}
}
}
#[derive(Clone, Copy)]
pub struct NativeCont {
pub kind: ContKind,
pub func_slot: u32,
pub nresults: i32,
}
#[derive(Clone, Copy)]
pub enum ContKind {
Pcall,
Xpcall {
handler: Value,
},
Meta(
MetaCont,
),
Pairs,
Close(
CloseCont,
),
}
#[derive(Clone, Copy)]
pub struct CloseCont {
pub from: u32,
pub pending: Option<Value>,
pub after: AfterClose,
}
#[derive(Clone, Copy)]
pub enum AfterClose {
Block,
Return {
abs_a: u32,
nret: u32,
from_native: bool,
},
ResumeUnwind {
func_slot: u32,
err: Value,
},
}
#[derive(Clone, Copy)]
pub struct MetaCont {
pub action: MetaAction,
pub saved_top: u32,
}
#[derive(Clone, Copy)]
pub enum MetaAction {
Store {
dst: u32,
},
Discard,
Compare {
k: bool,
negate: bool,
},
Concat {
dst: u32,
base_a: u32,
},
}
#[derive(Clone, Debug)]
pub struct UpvalDesc {
pub in_stack: bool,
pub index: u8,
pub name: Box<str>,
pub read_only: bool,
}
#[derive(Clone, Debug)]
pub struct LocVar {
pub name: Box<str>,
pub reg: u32,
pub start_pc: u32,
pub end_pc: u32,
}
#[repr(C)]
pub struct Proto {
pub(crate) hdr: GcHeader,
pub code: Box<[Inst]>,
pub consts: Box<[Value]>,
pub protos: Box<[Gc<Proto>]>,
pub upvals: Box<[UpvalDesc]>,
pub num_params: u8,
pub is_vararg: bool,
pub has_vararg_table_pseudo: bool,
pub has_compat_vararg_arg: bool,
pub max_stack: u8,
pub lines: Box<[u32]>,
pub source: Gc<LuaStr>,
pub line_defined: u32,
pub last_line_defined: u32,
pub locvars: Box<[LocVar]>,
pub cache: std::cell::Cell<Option<Gc<LuaClosure>>>,
pub env_upval_idx: u8,
pub jit: std::cell::Cell<JitProtoState>,
pub trace_hot_count: std::cell::Cell<u32>,
pub call_hot_count: std::cell::Cell<u32>,
pub trace_discard_count: std::cell::Cell<u32>,
pub trace_gave_up: std::cell::Cell<bool>,
pub traces: std::cell::RefCell<Vec<std::rc::Rc<crate::jit::trace::CompiledTrace>>>,
}
#[derive(Clone, Copy, Debug)]
pub enum JitProtoState {
Untried,
Failed,
Compiled {
entry: *const u8,
num_args: u8,
returns_one: bool,
arg_float_mask: u8,
arg_table_mask: u8,
ret_is_float: bool,
ret_is_table: bool,
},
}
struct FnvHash128 {
state: u128,
}
impl FnvHash128 {
const OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d;
const PRIME: u128 = 0x0000000001000000000000000000013b;
fn new() -> Self {
FnvHash128 {
state: Self::OFFSET_BASIS,
}
}
fn update(&mut self, bytes: &[u8]) {
let mut s = self.state;
for &b in bytes {
s ^= b as u128;
s = s.wrapping_mul(Self::PRIME);
}
self.state = s;
}
fn finish(self) -> [u8; 16] {
self.state.to_be_bytes()
}
}
impl Proto {
pub fn stable_hash(&self) -> [u8; 16] {
let mut h = FnvHash128::new();
for inst in self.code.iter() {
h.update(&inst.0.to_le_bytes());
}
for c in self.consts.iter() {
match c {
Value::Nil => h.update(&[0u8]),
Value::Bool(b) => {
h.update(&[1u8, *b as u8]);
}
Value::Int(i) => {
h.update(&[2u8]);
h.update(&i.to_le_bytes());
}
Value::Float(f) => {
h.update(&[3u8]);
h.update(&f.to_bits().to_le_bytes());
}
Value::Str(s) => {
h.update(&[4u8]);
let bytes = s.as_bytes();
h.update(&(bytes.len() as u32).to_le_bytes());
h.update(bytes);
}
Value::Table(_)
| Value::Closure(_)
| Value::Native(_)
| Value::Coro(_)
| Value::Userdata(_)
| Value::LightUserdata(_) => {
debug_assert!(
false,
"Proto::stable_hash: unexpected heap-pointer constant \
(kind={}); luna's compiler only emits nil/bool/number/string \
constants",
c.type_name()
);
h.update(&[255u8]);
}
}
}
for u in self.upvals.iter() {
h.update(&[u.in_stack as u8, u.index, u.read_only as u8]);
let name_bytes = u.name.as_bytes();
h.update(&(name_bytes.len() as u32).to_le_bytes());
h.update(name_bytes);
}
h.update(&[self.num_params, self.is_vararg as u8, self.max_stack]);
h.finish()
}
pub(crate) fn trace(&self, m: &mut Marker) {
for &k in self.consts.iter() {
m.value(k);
}
for &p in self.protos.iter() {
m.header(p.as_ptr() as *mut GcHeader);
}
m.header(self.source.as_ptr() as *mut GcHeader);
if self.cache.get().is_some() {
m.cached_protos.push(self as *const Proto as *mut Proto);
}
}
}
pub const INLINE_UPVALS_N: usize = 2;
#[repr(C)]
pub struct LuaClosure {
#[allow(dead_code)]
pub(crate) hdr: GcHeader,
pub proto: Gc<Proto>,
pub(crate) upvals_ptr: *mut Gc<Upvalue>,
pub(crate) upvals_len: u32,
pub(crate) inline_storage: [std::mem::MaybeUninit<Gc<Upvalue>>; INLINE_UPVALS_N],
pub(crate) overflow: Box<[Gc<Upvalue>]>,
}
unsafe impl Send for LuaClosure {}
unsafe impl Sync for LuaClosure {}
impl LuaClosure {
#[inline(always)]
pub fn upvals(&self) -> &[Gc<Upvalue>] {
unsafe { std::slice::from_raw_parts(self.upvals_ptr, self.upvals_len as usize) }
}
#[inline(always)]
pub(crate) fn upvals_mut(&mut self) -> &mut [Gc<Upvalue>] {
unsafe { std::slice::from_raw_parts_mut(self.upvals_ptr, self.upvals_len as usize) }
}
pub(crate) fn init_upvals_ptr(&mut self) {
if self.upvals_len as usize <= INLINE_UPVALS_N {
self.upvals_ptr = self.inline_storage.as_mut_ptr() as *mut Gc<Upvalue>;
} else {
self.upvals_ptr = self.overflow.as_mut_ptr();
}
}
pub(crate) fn trace(&self, m: &mut Marker) {
m.header(self.proto.as_ptr() as *mut GcHeader);
for &uv in self.upvals().iter() {
m.header(uv.as_ptr() as *mut GcHeader);
}
}
}
#[repr(C)]
pub struct NativeClosure {
#[allow(dead_code)]
pub(crate) hdr: GcHeader,
pub f: crate::runtime::value::NativeFn,
pub upvals: Box<[Value]>,
pub is_async: bool,
}
impl NativeClosure {
pub(crate) fn trace(&self, m: &mut Marker) {
for &v in self.upvals.iter() {
m.value(v);
}
}
}
#[repr(C)]
pub struct Upvalue {
#[allow(dead_code)]
pub(crate) hdr: GcHeader,
pub(crate) state: UpvalState,
}
#[derive(Clone, Copy)]
pub enum UpvalState {
Open {
slot: u32,
thread: Option<Gc<crate::runtime::coroutine::Coro>>,
},
Closed(
Value,
),
}
impl Upvalue {
pub fn state(&self) -> UpvalState {
self.state
}
pub(crate) fn set_closed(&mut self, v: Value) {
self.state = UpvalState::Closed(v);
}
pub(crate) fn trace(&self, m: &mut Marker) {
match self.state {
UpvalState::Closed(v) => {
m.value(v);
}
UpvalState::Open {
thread: Some(co), ..
} => {
m.header(co.as_ptr() as *mut GcHeader);
}
UpvalState::Open { thread: None, .. } => {}
}
}
}