1use std::cell::RefCell;
23use std::rc::Rc;
24
25use crate::string::StringPool;
26pub use lua_types::error::LuaError;
27pub use lua_types::{CallInfoIdx, StackIdx};
28
29pub struct StackIdxConv(pub StackIdx);
32
33#[inline(always)]
38pub fn stack_idx_to_i32(i: StackIdx) -> i32 { i.0 as i32 }
39
40impl From<u32> for StackIdxConv {
41 #[inline(always)]
42 fn from(v: u32) -> Self { StackIdxConv(StackIdx(v)) }
43}
44impl From<i32> for StackIdxConv {
45 #[inline(always)]
46 fn from(v: i32) -> Self { StackIdxConv(StackIdx(v.max(0) as u32)) }
47}
48impl From<usize> for StackIdxConv {
49 #[inline(always)]
50 fn from(v: usize) -> Self { StackIdxConv(StackIdx(v as u32)) }
51}
52impl From<StackIdx> for StackIdxConv {
53 #[inline(always)]
54 fn from(v: StackIdx) -> Self { StackIdxConv(v) }
55}
56pub use lua_types::value::{LuaTable, LuaValue, F2Imod};
57pub use lua_types::string::LuaString;
58pub use lua_types::userdata::LuaUserData;
59pub use lua_types::closure::{LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua, LuaCClosure as LuaClosureC};
60pub use lua_types::proto::LuaProto;
61pub use lua_types::upval::{UpVal, UpValState};
62pub use lua_types::gc::GcRef;
63
64pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
71
72pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
73
74#[derive(Clone)]
75pub enum LuaCallable {
76 Bare(LuaCFunction),
77 Rust(LuaRustFunction),
78}
79
80impl std::fmt::Debug for LuaCallable {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
84 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
85 }
86 }
87}
88
89impl LuaCallable {
90 pub fn bare(f: LuaCFunction) -> Self {
91 LuaCallable::Bare(f)
92 }
93
94 pub fn rust(f: LuaRustFunction) -> Self {
95 LuaCallable::Rust(f)
96 }
97
98 pub fn as_bare(&self) -> Option<LuaCFunction> {
99 match self {
100 LuaCallable::Bare(f) => Some(*f),
101 LuaCallable::Rust(_) => None,
102 }
103 }
104
105 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
106 match self {
107 LuaCallable::Bare(f) => f(state),
108 LuaCallable::Rust(f) => f(state),
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
114pub enum FinalizerObject {
115 Table(GcRef<LuaTable>),
116 UserData(GcRef<LuaUserData>),
117}
118
119impl FinalizerObject {
120 pub fn identity(&self) -> usize {
121 match self {
122 FinalizerObject::Table(t) => t.identity(),
123 FinalizerObject::UserData(u) => u.identity(),
124 }
125 }
126
127 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
128 match self {
129 FinalizerObject::Table(t) => t.metatable(),
130 FinalizerObject::UserData(u) => u.metatable(),
131 }
132 }
133
134 pub fn as_lua_value(&self) -> LuaValue {
135 match self {
136 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
137 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
138 }
139 }
140
141 pub fn mark(&self, marker: &mut lua_gc::Marker) {
142 match self {
143 FinalizerObject::Table(t) => marker.mark(t.0),
144 FinalizerObject::UserData(u) => marker.mark(u.0),
145 }
146 }
147}
148
149pub(crate) const EXTRA_STACK: usize = 5;
153
154pub(crate) const LUA_MINSTACK: usize = 20;
156
157pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
159
160pub(crate) const LUAI_MAXCCALLS: u32 = 200;
180
181pub(crate) const CIST_C: u16 = 1 << 1;
183
184pub(crate) const CIST_OAH: u16 = 1 << 0;
186pub(crate) const CIST_FRESH: u16 = 1 << 2;
187pub(crate) const CIST_HOOKED: u16 = 1 << 3;
188pub(crate) const CIST_YPCALL: u16 = 1 << 4;
189pub(crate) const CIST_TAIL: u16 = 1 << 5;
190pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
191pub(crate) const CIST_FIN: u16 = 1 << 7;
192pub(crate) const CIST_TRAN: u16 = 1 << 8;
193pub(crate) const CIST_RECST: u32 = 10;
194pub(crate) const CIST_LEQ: u16 = 1 << 13;
201
202const LUA_NUMTYPES: usize = 9;
204
205const GCSTPUSR: u8 = 1;
207const GCSTPGC: u8 = 2;
208
209const GCS_PAUSE: u8 = 0;
211
212const LUAI_GCPAUSE: u32 = 200;
213const LUAI_GCMUL: u32 = 100;
214const LUAI_GCSTEPSIZE: u8 = 13;
215const LUAI_GENMAJORMUL: u32 = 100;
216const LUAI_GENMINORMUL: u8 = 20;
217
218const WHITE0BIT: u8 = 0;
219
220const STRCACHE_N: usize = 53;
221const STRCACHE_M: usize = 2;
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub enum GcKind {
230 Incremental = 0,
231 Generational = 1,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum WarnMode {
243 Off,
244 On,
245 Cont,
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub enum TestWarnMode {
251 Normal,
252 Allow,
253 Store,
254}
255
256pub use lua_types::status::LuaStatus;
261
262#[derive(Clone)]
269pub struct StackValue {
270 pub val: LuaValue,
271 pub tbc_delta: u16,
272}
273
274impl Default for StackValue {
275 fn default() -> Self {
276 StackValue {
277 val: LuaValue::Nil,
278 tbc_delta: 0,
279 }
280 }
281}
282
283#[derive(Clone)]
292pub struct CallInfo {
293 pub func: StackIdx,
295
296 pub top: StackIdx,
298
299 pub previous: Option<CallInfoIdx>,
301
302 pub next: Option<CallInfoIdx>,
304
305 pub u: CallInfoFrame,
306
307 pub u2: CallInfoExtra,
308
309 pub nresults: i16,
311
312 pub callstatus: u16,
314}
315
316#[derive(Clone, Copy)]
319pub enum CallInfoFrame {
320 Lua {
321 savedpc: u32,
323 trap: bool,
325 nextraargs: i32,
327 },
328 C {
329 k: Option<LuaKFunction>,
331 old_errfunc: isize,
333 ctx: isize,
335 },
336}
337
338pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
340
341#[derive(Default, Clone, Copy)]
345pub struct CallInfoExtra {
346 pub value: i32,
347 pub ftransfer: u16,
348 pub ntransfer: u16,
349}
350
351impl CallInfoFrame {
352 pub fn c_default() -> Self {
354 CallInfoFrame::C {
355 k: None,
356 old_errfunc: 0,
357 ctx: 0,
358 }
359 }
360
361 pub fn lua_default() -> Self {
363 CallInfoFrame::Lua {
364 savedpc: 0,
365 trap: false,
366 nextraargs: 0,
367 }
368 }
369}
370
371impl Default for CallInfo {
372 fn default() -> Self {
373 CallInfo {
374 func: StackIdx(0),
375 top: StackIdx(0),
376 previous: None,
377 next: None,
378 u: CallInfoFrame::c_default(),
379 u2: CallInfoExtra::default(),
380 nresults: 0,
381 callstatus: 0,
382 }
383 }
384}
385
386impl CallInfo {
387 pub fn is_lua(&self) -> bool { (self.callstatus & CIST_C) == 0 }
388 pub fn is_lua_code(&self) -> bool { self.is_lua() }
389 pub fn is_vararg_func(&self) -> bool { false }
396 pub fn saved_pc(&self) -> u32 {
397 if let CallInfoFrame::Lua { savedpc, .. } = self.u { savedpc } else { 0 }
398 }
399 pub fn set_saved_pc(&mut self, pc: u32) {
400 if let CallInfoFrame::Lua { ref mut savedpc, .. } = self.u { *savedpc = pc; }
401 }
402 pub fn nextra_args(&self) -> i32 {
403 if let CallInfoFrame::Lua { nextraargs, .. } = self.u { nextraargs } else { 0 }
404 }
405 pub fn transfer_ftransfer(&self) -> u16 { self.u2.ftransfer }
406 pub fn transfer_ntransfer(&self) -> u16 { self.u2.ntransfer }
407 pub fn set_trap(&mut self, t: bool) {
408 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u { *trap = t; }
409 }
410 pub fn recover_status(&self) -> i32 {
413 ((self.callstatus >> CIST_RECST) & 7) as i32
414 }
415 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
418 let st = (status.into() & 7) as u16;
419 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
420 }
421 pub fn get_oah(&self) -> bool { (self.callstatus & CIST_OAH) != 0 }
422 pub fn set_oah(&mut self, allow: bool) {
425 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
426 }
427 pub fn u_c_old_errfunc(&self) -> isize {
428 if let CallInfoFrame::C { old_errfunc, .. } = self.u { old_errfunc } else { 0 }
429 }
430 pub fn u_c_ctx(&self) -> isize {
431 if let CallInfoFrame::C { ctx, .. } = self.u { ctx } else { 0 }
432 }
433 pub fn u_c_k(&self) -> Option<LuaKFunction> {
434 if let CallInfoFrame::C { k, .. } = self.u { k } else { None }
435 }
436 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
440 if let CallInfoFrame::C { k: ref mut slot, .. } = self.u {
441 *slot = k;
442 }
443 }
444 pub fn set_u_c_ctx(&mut self, ctx: isize) {
446 if let CallInfoFrame::C { ctx: ref mut slot, .. } = self.u {
447 *slot = ctx;
448 }
449 }
450 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
452 if let CallInfoFrame::C { old_errfunc: ref mut slot, .. } = self.u {
453 *slot = old_errfunc;
454 }
455 }
456 pub fn set_u2_funcidx(&mut self, idx: i32) {
459 self.u2.value = idx;
460 }
461}
462
463pub trait LuaValueExt {
469 fn base_type(&self) -> lua_types::LuaType;
470 fn to_number_no_strconv(&self) -> Option<f64>;
471 fn to_number_with_strconv(&self) -> Option<f64>;
472 fn to_integer_no_strconv(&self) -> Option<i64>;
473 fn to_integer_with_strconv(&self) -> Option<i64>;
474 fn full_type_tag(&self) -> u8;
475}
476
477impl LuaValueExt for LuaValue {
478 fn base_type(&self) -> lua_types::LuaType { self.type_tag() }
479 fn to_number_no_strconv(&self) -> Option<f64> {
480 match self {
481 LuaValue::Float(f) => Some(*f),
482 LuaValue::Int(i) => Some(*i as f64),
483 _ => None,
484 }
485 }
486 fn to_number_with_strconv(&self) -> Option<f64> {
487 if let Some(n) = self.to_number_no_strconv() { return Some(n); }
488 if let LuaValue::Str(s) = self {
489 let mut tmp = LuaValue::Nil;
490 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
491 if sz == 0 { return None; }
492 return match tmp {
493 LuaValue::Int(i) => Some(i as f64),
494 LuaValue::Float(f) => Some(f),
495 _ => None,
496 };
497 }
498 None
499 }
500 fn to_integer_no_strconv(&self) -> Option<i64> {
501 match self {
502 LuaValue::Int(i) => Some(*i),
503 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
504 let min_f = i64::MIN as f64;
508 let max_plus1_f = -(i64::MIN as f64);
509 if *f >= min_f && *f < max_plus1_f {
510 Some(*f as i64)
511 } else {
512 None
513 }
514 }
515 _ => None,
516 }
517 }
518 fn to_integer_with_strconv(&self) -> Option<i64> {
519 if let Some(i) = self.to_integer_no_strconv() { return Some(i); }
520 if let LuaValue::Str(s) = self {
521 let mut tmp = LuaValue::Nil;
522 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
523 if sz == 0 { return None; }
524 return tmp.to_integer_no_strconv();
525 }
526 None
527 }
528 fn full_type_tag(&self) -> u8 {
529 match self {
530 LuaValue::Nil => 0x00,
531 LuaValue::Bool(false) => 0x01,
532 LuaValue::Bool(true) => 0x11,
533 LuaValue::Int(_) => 0x03,
534 LuaValue::Float(_) => 0x13,
535 LuaValue::Str(s) if s.is_short() => 0x04,
536 LuaValue::Str(_) => 0x14,
537 LuaValue::LightUserData(_) => 0x02,
538 LuaValue::Table(_) => 0x05,
539 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
540 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
541 LuaValue::Function(LuaClosure::C(_)) => 0x26,
542 LuaValue::UserData(_) => 0x07,
543 LuaValue::Thread(_) => 0x08,
544 }
545 }
546}
547
548pub trait LuaTypeExt {
550 fn type_name(&self) -> &'static [u8];
551}
552
553impl LuaTypeExt for lua_types::LuaType {
554 fn type_name(&self) -> &'static [u8] {
555 use lua_types::LuaType::*;
556 match self {
557 None => b"no value",
558 Nil => b"nil",
559 Boolean => b"boolean",
560 LightUserData => b"userdata",
561 Number => b"number",
562 String => b"string",
563 Table => b"table",
564 Function => b"function",
565 UserData => b"userdata",
566 Thread => b"thread",
567 }
568 }
569}
570
571pub trait StackIdxExt {
575 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
576 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
577 fn raw(self) -> u32;
578}
579impl StackIdxExt for StackIdx {
580 #[inline(always)]
581 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.saturating_sub(n.into().0.0) }
582 #[inline(always)]
583 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.wrapping_sub(n.into().0.0) }
584 #[inline(always)]
585 fn raw(self) -> u32 { self.0 }
586}
587
588pub trait LuaTableRefExt {
598 fn metatable(&self) -> Option<GcRef<LuaTable>>;
599 fn as_ptr(&self) -> *const ();
600 fn get(&self, _k: &LuaValue) -> LuaValue;
601 fn get_int(&self, _k: i64) -> LuaValue;
602 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
603 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
604 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
605 fn invalidate_tm_cache(&self);
606 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
607 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
608}
609impl LuaTableRefExt for GcRef<LuaTable> {
610 #[inline]
611 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
612 #[inline]
613 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
614 #[inline]
615 fn get(&self, k: &LuaValue) -> LuaValue { (**self).get(k) }
616 #[inline]
617 fn get_int(&self, k: i64) -> LuaValue { (**self).get_int(k) }
618 #[inline]
619 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue { (**self).get_short_str(k) }
620 #[inline]
623 fn raw_set(&self, _state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
624 let before = (**self).buffer_bytes();
625 let result = (**self).try_raw_set(k, v);
626 if result.is_ok() {
627 account_table_buffer_delta(self, before);
628 }
629 result
630 }
631 #[inline]
632 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
633 let before = (**self).buffer_bytes();
634 let result = (**self).try_raw_set_int(k, v);
635 if result.is_ok() {
636 account_table_buffer_delta(self, before);
637 }
638 result
639 }
640 fn invalidate_tm_cache(&self) {}
641 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
642 let before = (**self).buffer_bytes();
643 let na32 = na.min(u32::MAX as usize) as u32;
644 let nh32 = nh.min(u32::MAX as usize) as u32;
645 let result = (**self).resize(na32, nh32);
646 if result.is_ok() {
647 account_table_buffer_delta(self, before);
648 }
649 result
650 }
651 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
652 (**self).try_next_pair(&k)
653 }
654}
655
656#[inline]
657fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
658 let after = (**t).buffer_bytes();
659 if after > before {
660 t.account_buffer((after - before) as isize);
661 } else if before > after {
662 t.account_buffer(-((before - after) as isize));
663 }
664}
665
666pub trait LuaUserDataRefExt {
667 fn metatable(&self) -> Option<GcRef<LuaTable>>;
668 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
669 fn as_ptr(&self) -> *const ();
670 fn len(&self) -> usize;
671}
672impl LuaUserDataRefExt for GcRef<LuaUserData> {
673 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
674 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) { (**self).set_metatable(mt); }
675 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
676 fn len(&self) -> usize { self.0.data.len() }
677}
678
679pub trait LuaStringRefExt {
680 fn is_white(&self) -> bool;
681 fn hash(&self) -> u32;
682 fn as_gc_ref(&self) -> GcRef<LuaString>;
683}
684impl LuaStringRefExt for GcRef<LuaString> {
685 fn is_white(&self) -> bool { false }
686 fn hash(&self) -> u32 { self.0.hash() }
687 fn as_gc_ref(&self) -> GcRef<LuaString> { self.clone() }
688}
689
690pub trait LuaLClosureRefExt {
691 fn proto(&self) -> &GcRef<LuaProto>;
692 fn nupvalues(&self) -> usize;
693}
694impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
695 fn proto(&self) -> &GcRef<LuaProto> { &self.0.proto }
696 fn nupvalues(&self) -> usize { self.0.upvals.len() }
697}
698
699pub trait LuaClosureExt {
701 fn nupvalues(&self) -> usize;
702}
703impl LuaClosureExt for LuaClosure {
704 fn nupvalues(&self) -> usize {
705 match self {
706 LuaClosure::Lua(l) => l.0.upvals.len(),
707 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
708 LuaClosure::LightC(_) => 0,
709 }
710 }
711}
712
713pub trait LuaProtoExt {
715 fn source_bytes(&self) -> &[u8];
716 fn source_string(&self) -> Option<&GcRef<LuaString>>;
717}
718impl LuaProtoExt for LuaProto {
719 fn source_bytes(&self) -> &[u8] {
720 match &self.source { Some(s) => s.0.as_bytes(), None => &[] }
721 }
722 fn source_string(&self) -> Option<&GcRef<LuaString>> { self.source.as_ref() }
723}
724
725pub trait Collectable: std::fmt::Debug {}
732
733impl std::fmt::Debug for LuaState {
734 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
735 write!(f, "LuaState")
736 }
737}
738impl Collectable for LuaState {}
739
740pub type ParserHook = fn(
749 state: &mut LuaState,
750 source: &[u8],
751 name: &[u8],
752 firstchar: i32,
753) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
754
755pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
763
764pub type FileOpenHook =
775 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
776
777pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
785
786pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
789
790pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
796
797pub type UnixTimeHook = fn() -> i64;
799
800pub type CpuClockHook = fn() -> f64;
808
809pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
821
822pub type EntropyHook = fn() -> u64;
826
827pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
832
833pub type PopenHook =
844 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
845
846pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
852
853pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
859
860#[derive(Clone, Copy, Debug)]
866pub enum OsExecuteReason {
867 Exit,
869 Signal,
871}
872
873#[derive(Debug)]
876pub struct OsExecuteResult {
877 pub success: bool,
879 pub reason: OsExecuteReason,
881 pub code: i32,
883}
884
885pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
892
893#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
900pub struct DynLibId(pub u64);
901
902pub enum DynamicSymbol {
910 RustNative(LuaCFunction),
913 LuaCAbi(*const ()),
919 Unsupported { reason: Vec<u8> },
922}
923
924pub type DynLibLoadHook =
935 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
936
937pub type DynLibSymbolHook =
945 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
946
947pub type DynLibUnloadHook = fn(handle: DynLibId);
955
956pub struct ThreadRegistryEntry {
962 pub state: Rc<RefCell<LuaState>>,
967 pub value: GcRef<lua_types::value::LuaThread>,
970}
971
972#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
977pub struct ExternalRootKey {
978 index: usize,
979 generation: u64,
980}
981
982#[derive(Debug)]
983struct ExternalRootSlot {
984 value: Option<LuaValue>,
985 generation: u64,
986}
987
988#[derive(Debug, Default)]
994pub struct ExternalRootSet {
995 slots: Vec<ExternalRootSlot>,
996 free: Vec<usize>,
997 live: usize,
998}
999
1000impl ExternalRootSet {
1001 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1002 if let Some(index) = self.free.pop() {
1003 let slot = &mut self.slots[index];
1004 debug_assert!(
1005 slot.value.is_none(),
1006 "free external-root slot is occupied"
1007 );
1008 slot.generation = slot.generation.wrapping_add(1).max(1);
1009 slot.value = Some(value);
1010 self.live += 1;
1011 ExternalRootKey {
1012 index,
1013 generation: slot.generation,
1014 }
1015 } else {
1016 let index = self.slots.len();
1017 self.slots.push(ExternalRootSlot {
1018 value: Some(value),
1019 generation: 1,
1020 });
1021 self.live += 1;
1022 ExternalRootKey {
1023 index,
1024 generation: 1,
1025 }
1026 }
1027 }
1028
1029 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1030 let slot = self.slots.get(key.index)?;
1031 if slot.generation == key.generation {
1032 slot.value.as_ref()
1033 } else {
1034 None
1035 }
1036 }
1037
1038 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1039 let slot = self.slots.get_mut(key.index)?;
1040 if slot.generation != key.generation || slot.value.is_none() {
1041 return None;
1042 }
1043 slot.value.replace(value)
1044 }
1045
1046 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1047 let slot = self.slots.get_mut(key.index)?;
1048 if slot.generation != key.generation {
1049 return None;
1050 }
1051 let old = slot.value.take()?;
1052 self.free.push(key.index);
1053 self.live -= 1;
1054 Some(old)
1055 }
1056
1057 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1058 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1059 }
1060
1061 pub fn len(&self) -> usize {
1062 self.live
1063 }
1064
1065 pub fn is_empty(&self) -> bool {
1066 self.live == 0
1067 }
1068
1069 pub fn vacant_len(&self) -> usize {
1070 self.free.len()
1071 }
1072}
1073
1074pub struct GlobalState {
1080 pub parser_hook: Option<ParserHook>,
1086
1087 pub cli_argv: Option<Vec<Vec<u8>>>,
1094
1095 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1099
1100 pub lua_version: lua_types::LuaVersion,
1107
1108 pub file_loader_hook: Option<FileLoaderHook>,
1113
1114 pub file_open_hook: Option<FileOpenHook>,
1119
1120 pub stdout_hook: Option<OutputHook>,
1124
1125 pub stderr_hook: Option<OutputHook>,
1127
1128 pub stdin_hook: Option<InputHook>,
1131
1132 pub env_hook: Option<EnvHook>,
1134
1135 pub unix_time_hook: Option<UnixTimeHook>,
1138
1139 pub cpu_clock_hook: Option<CpuClockHook>,
1142
1143 pub local_offset_hook: Option<LocalOffsetHook>,
1148
1149 pub entropy_hook: Option<EntropyHook>,
1152
1153 pub temp_name_hook: Option<TempNameHook>,
1155
1156 pub popen_hook: Option<PopenHook>,
1161
1162 pub file_remove_hook: Option<FileRemoveHook>,
1165
1166 pub file_rename_hook: Option<FileRenameHook>,
1169
1170 pub os_execute_hook: Option<OsExecuteHook>,
1174
1175 pub dynlib_load_hook: Option<DynLibLoadHook>,
1180
1181 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1185
1186 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1190
1191 pub totalbytes: isize,
1193
1194 pub sandbox: SandboxLimits,
1197
1198 pub gc_debt: isize,
1200
1201 pub gc_estimate: usize,
1202
1203 pub lastatomic: usize,
1205
1206 pub strt: StringPool,
1208
1209 pub l_registry: LuaValue,
1211
1212 pub external_roots: ExternalRootSet,
1215
1216 pub globals: LuaValue,
1223 pub loaded: LuaValue,
1224
1225 pub nilvalue: LuaValue,
1229
1230 pub seed: u32,
1232
1233 pub currentwhite: u8,
1235
1236 pub gcstate: u8,
1237
1238 pub gckind: u8,
1239
1240 pub gcstopem: bool,
1241
1242 pub genminormul: u8,
1244
1245 pub genmajormul: u8,
1246
1247 pub gcstp: u8,
1248
1249 pub gcemergency: bool,
1250
1251 pub gcpause: u8,
1253
1254 pub gcstepmul: u8,
1256
1257 pub gcstepsize: u8,
1258
1259 pub gc55_params: [i64; 6],
1268
1269 pub sweepgc_cursor: usize,
1277
1278 pub weak_tables_registry: Vec<lua_types::gc::GcWeak<lua_types::value::LuaTable>>,
1287
1288 pub pending_finalizers: Vec<FinalizerObject>,
1293
1294 pub to_be_finalized: Vec<FinalizerObject>,
1299
1300 pub gc_finalizer_error: Option<LuaValue>,
1311
1312 pub twups: Vec<GcRef<LuaState>>,
1323
1324 pub panic: Option<LuaCFunction>,
1326
1327 pub mainthread: Option<GcRef<LuaState>>,
1330
1331 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1344
1345 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1350
1351 pub current_thread_id: u64,
1356
1357 pub main_thread_id: u64,
1360
1361 pub next_thread_id: u64,
1364
1365 pub memerrmsg: GcRef<LuaString>,
1367
1368 pub tmname: Vec<GcRef<LuaString>>,
1371
1372 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1374
1375 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1377
1378 pub interned_lt: std::collections::HashMap<Box<[u8]>, GcRef<LuaString>>,
1384
1385 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1387
1388 pub warn_mode: WarnMode,
1393
1394 pub test_warn_enabled: bool,
1399 pub test_warn_on: bool,
1400 pub test_warn_mode: TestWarnMode,
1401 pub test_warn_last_to_cont: bool,
1402 pub test_warn_buffer: Vec<u8>,
1403
1404 pub c_functions: Vec<LuaCallable>,
1409
1410 pub heap: lua_gc::Heap,
1415
1416 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1430
1431 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1445
1446 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1452}
1453
1454const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1457
1458pub const SANDBOX_TRIP_NONE: u8 = 0;
1460pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1462pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1464
1465#[derive(Default)]
1472pub struct SandboxLimits {
1473 pub interval: std::cell::Cell<i32>,
1475 pub instr_limited: std::cell::Cell<bool>,
1477 pub instr_remaining: std::cell::Cell<u64>,
1479 pub instr_limit: std::cell::Cell<u64>,
1481 pub mem_limit: std::cell::Cell<Option<usize>>,
1483 pub tripped: std::cell::Cell<u8>,
1485 pub aborting: std::cell::Cell<bool>,
1490}
1491
1492impl GlobalState {
1493 pub fn sandbox_active(&self) -> bool {
1495 self.sandbox.interval.get() != 0
1496 }
1497
1498 pub fn total_bytes(&self) -> usize {
1503 self.heap.bytes_used().max(1)
1504 }
1505
1506 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1511 self.threads.get(&id)
1512 }
1513
1514 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1518 if id == self.main_thread_id {
1519 Some(self.main_thread_value.clone())
1520 } else {
1521 self.threads.get(&id).map(|e| e.value.clone())
1522 }
1523 }
1524
1525 pub fn is_complete(&self) -> bool {
1532 matches!(self.nilvalue, LuaValue::Nil)
1533 }
1534
1535 pub fn current_white(&self) -> u8 {
1543 self.currentwhite
1544 }
1545
1546 pub fn other_white(&self) -> u8 {
1550 self.currentwhite ^ 0x03
1551 }
1552
1553 pub fn is_gen_mode(&self) -> bool {
1557 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1558 }
1559
1560 pub fn gc_running(&self) -> bool {
1564 self.gcstp == 0
1565 }
1566
1567 pub fn keep_invariant(&self) -> bool {
1571 matches!(
1572 self.heap.gc_state(),
1573 lua_gc::GcState::Propagate | lua_gc::GcState::Atomic
1574 )
1575 }
1576
1577 pub fn is_sweep_phase(&self) -> bool {
1581 self.heap.gc_state().is_sweep()
1582 }
1583
1584 pub fn gc_debt(&self) -> isize { self.gc_debt }
1586 pub fn set_gc_debt(&mut self, d: isize) { self.gc_debt = d; }
1587 pub fn gc_at_pause(&self) -> bool { self.heap.gc_state().is_pause() }
1588 fn get_gc_param(p: u8) -> i32 { (p as i32) * 4 }
1589 fn set_gc_param_slot(slot: &mut u8, p: i32) { *slot = (p / 4) as u8; }
1590 pub fn gc_pause_param(&self) -> i32 { Self::get_gc_param(self.gcpause) }
1591 pub fn set_gc_pause_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcpause, p); }
1592 pub fn gc_stepmul_param(&self) -> i32 { Self::get_gc_param(self.gcstepmul) }
1593 pub fn set_gc_stepmul_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcstepmul, p); }
1594 pub fn gc_genmajormul_param(&self) -> i32 { Self::get_gc_param(self.genmajormul) }
1595 pub fn set_gc_genmajormul(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.genmajormul, p); }
1596 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1600 let old = self.gc55_params[idx];
1601 if value >= 0 {
1602 self.gc55_params[idx] = value;
1603 }
1604 old
1605 }
1606 pub fn gc_stop_flags(&self) -> u8 { self.gcstp }
1607 pub fn set_gc_stop_flags(&mut self, f: u8) { self.gcstp = f; }
1608 pub fn stop_gc_internal(&mut self) -> u8 {
1609 let old = self.gcstp;
1610 self.gcstp |= GCSTPGC;
1611 old
1612 }
1613 pub fn set_gc_stop_user(&mut self) {
1614 self.gcstp = GCSTPUSR;
1616 }
1617 pub fn clear_gc_stop(&mut self) { self.gcstp = 0; }
1618 pub fn is_gc_running(&self) -> bool { self.gcstp == 0 }
1619 pub fn is_gc_stopped_internally(&self) -> bool { (self.gcstp & GCSTPGC) != 0 }
1624
1625 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1635 self.tmname.get(tm.tm_index()).cloned()
1636 }
1637}
1638
1639pub trait TmIndex: Copy {
1645 fn tm_index(self) -> usize;
1646}
1647impl TmIndex for lua_types::tagmethod::TagMethod {
1648 fn tm_index(self) -> usize { self as u8 as usize }
1649}
1650impl TmIndex for crate::tagmethods::TagMethod {
1651 fn tm_index(self) -> usize { self as u8 as usize }
1652}
1653impl TmIndex for usize {
1654 fn tm_index(self) -> usize { self }
1655}
1656impl TmIndex for u8 {
1657 fn tm_index(self) -> usize { self as usize }
1658}
1659
1660use lua_types::tagmethod::TagMethod;
1661
1662pub struct LuaState {
1672 pub status: u8,
1676
1677 pub allowhook: bool,
1679
1680 pub nci: u32,
1682
1683 pub top: StackIdx,
1687
1688 pub stack_last: StackIdx,
1690
1691 pub stack: Vec<StackValue>,
1693
1694 pub ci: CallInfoIdx,
1698
1699 pub call_info: Vec<CallInfo>,
1702
1703 pub openupval: Vec<GcRef<UpVal>>,
1707
1708 pub tbclist: Vec<StackIdx>,
1710
1711 pub(crate) global: Rc<RefCell<GlobalState>>,
1716
1717 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
1721
1722 pub hookmask: u8,
1724
1725 pub basehookcount: i32,
1727
1728 pub hookcount: i32,
1730
1731 pub errfunc: isize,
1738
1739 pub n_ccalls: u32,
1743
1744 pub oldpc: u32,
1748
1749 pub marked: u8,
1753
1754 pub cached_thread_id: u64,
1770
1771 pub gc_check_needed: bool,
1776
1777}
1778
1779impl LuaState {
1780 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
1788 self.global.borrow()
1789 }
1790
1791 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
1795 self.global.borrow_mut()
1796 }
1797
1798 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
1802 Rc::clone(&self.global)
1803 }
1804
1805 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
1807 {
1808 let mut g = self.global_mut();
1809 g.test_warn_enabled = true;
1810 g.test_warn_on = false;
1811 g.test_warn_mode = TestWarnMode::Normal;
1812 g.test_warn_last_to_cont = false;
1813 g.test_warn_buffer.clear();
1814 }
1815 self.push(LuaValue::Bool(false));
1816 crate::api::set_global(self, b"_WARN")
1817 }
1818
1819 pub fn c_calls(&self) -> u32 {
1823 self.n_ccalls & 0xffff
1824 }
1825
1826 pub fn inc_nny(&mut self) {
1830 self.n_ccalls += 0x10000;
1831 }
1832
1833 pub fn dec_nny(&mut self) {
1837 self.n_ccalls -= 0x10000;
1838 }
1839
1840 pub fn is_yieldable(&self) -> bool {
1844 (self.n_ccalls & 0xffff0000) == 0
1845 }
1846
1847 pub fn reset_hook_count(&mut self) {
1851 self.hookcount = self.basehookcount;
1852 }
1853
1854 pub fn install_sandbox_limits(
1863 &mut self,
1864 interval: i32,
1865 instr_limit: Option<u64>,
1866 mem_limit: Option<usize>,
1867 ) {
1868 let interval = interval.max(1);
1869 {
1870 let g = self.global();
1871 g.sandbox.interval.set(interval);
1872 g.sandbox.instr_limited.set(instr_limit.is_some());
1873 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
1874 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
1875 g.sandbox.mem_limit.set(mem_limit);
1876 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
1877 }
1878 self.hookmask |= SANDBOX_COUNT_MASK;
1879 self.basehookcount = interval;
1880 self.hookcount = interval;
1881 crate::debug::arm_traps(self);
1882 }
1883
1884 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
1889 let interval = self.global().sandbox.interval.get();
1890 self.sandbox_charge(interval as u64)
1891 }
1892
1893 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
1902 let g = self.global();
1903 if g.sandbox.interval.get() == 0 {
1904 return None;
1905 }
1906 if g.sandbox.instr_limited.get() {
1907 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
1908 g.sandbox.instr_remaining.set(rem);
1909 if rem == 0 {
1910 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
1911 g.sandbox.aborting.set(true);
1912 return Some(LuaError::runtime(format_args!(
1913 "sandbox: instruction budget exhausted"
1914 )));
1915 }
1916 }
1917 if let Some(limit) = g.sandbox.mem_limit.get() {
1918 if g.total_bytes() > limit {
1919 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
1920 g.sandbox.aborting.set(true);
1921 return Some(LuaError::runtime(format_args!(
1922 "sandbox: memory limit exceeded"
1923 )));
1924 }
1925 }
1926 None
1927 }
1928
1929 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
1937 let g = self.global();
1938 if g.sandbox.interval.get() == 0 {
1939 return None;
1940 }
1941 if let Some(limit) = g.sandbox.mem_limit.get() {
1942 let projected = g.total_bytes().saturating_add(additional);
1943 if projected > limit {
1944 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
1945 g.sandbox.aborting.set(true);
1946 return Some(LuaError::runtime(format_args!(
1947 "sandbox: memory limit exceeded"
1948 )));
1949 }
1950 }
1951 None
1952 }
1953
1954 pub fn sandbox_match_step_limit(&self) -> u64 {
1959 let g = self.global();
1960 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
1961 g.sandbox.instr_remaining.get()
1962 } else {
1963 0
1964 }
1965 }
1966
1967 pub fn sandbox_aborting(&self) -> bool {
1971 self.global().sandbox.aborting.get()
1972 }
1973
1974 pub fn sandbox_instr_limited(&self) -> bool {
1976 self.global().sandbox.instr_limited.get()
1977 }
1978
1979 pub fn sandbox_instr_remaining(&self) -> u64 {
1982 self.global().sandbox.instr_remaining.get()
1983 }
1984
1985 pub fn sandbox_instr_limit(&self) -> u64 {
1987 self.global().sandbox.instr_limit.get()
1988 }
1989
1990 pub fn sandbox_tripped_code(&self) -> u8 {
1992 self.global().sandbox.tripped.get()
1993 }
1994
1995 pub fn sandbox_reset(&self) {
1998 let g = self.global();
1999 if g.sandbox.instr_limited.get() {
2000 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2001 }
2002 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2003 g.sandbox.aborting.set(false);
2004 }
2005
2006 pub fn stack_size(&self) -> usize {
2010 self.stack_last.0 as usize
2011 }
2012
2013 #[inline(always)]
2017 pub fn push(&mut self, val: LuaValue) {
2018 let top = self.top.0 as usize;
2019 if top < self.stack.len() {
2020 self.stack[top] = StackValue { val, tbc_delta: 0 };
2021 } else {
2022 self.stack.push(StackValue { val, tbc_delta: 0 });
2023 }
2024 self.top = StackIdx(self.top.0 + 1);
2025 }
2026
2027 #[inline(always)]
2030 pub fn pop(&mut self) -> LuaValue {
2031 if self.top.0 == 0 {
2032 return LuaValue::Nil;
2033 }
2034 self.top = StackIdx(self.top.0 - 1);
2035 self.stack[self.top.0 as usize].val.clone()
2036 }
2037
2038 #[inline(always)]
2042 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2043 &self.stack[idx.0 as usize].val
2044 }
2045
2046 #[inline(always)]
2048 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2049 self.stack[idx.0 as usize].val = val;
2050 }
2051
2052 pub fn gc(&mut self) -> GcHandle<'_> {
2059 GcHandle { _state: self }
2060 }
2061
2062 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2064 self.global_mut().external_roots.insert(value)
2065 }
2066
2067 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2069 self.global().external_roots.get(key).cloned()
2070 }
2071
2072 pub fn external_replace_root(
2074 &mut self,
2075 key: ExternalRootKey,
2076 value: LuaValue,
2077 ) -> Option<LuaValue> {
2078 self.global_mut().external_roots.replace(key, value)
2079 }
2080
2081 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2083 self.global_mut().external_roots.remove(key)
2084 }
2085
2086 pub fn try_external_unroot_value(
2089 &mut self,
2090 key: ExternalRootKey,
2091 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2092 self.global
2093 .try_borrow_mut()
2094 .map(|mut global| global.external_roots.remove(key))
2095 }
2096
2097 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2101 self.mark_gc_check_needed();
2103 GcRef::new(LuaTable::placeholder())
2104 }
2105
2106 pub fn new_table_with_sizes(
2111 &mut self,
2112 array_size: u32,
2113 hash_size: u32,
2114 ) -> Result<GcRef<LuaTable>, LuaError> {
2115 self.mark_gc_check_needed();
2116 let t = GcRef::new(LuaTable::placeholder());
2117 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2118 Ok(t)
2119 }
2120
2121 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2132 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2133 if let Some(existing) = self.global().interned_lt.get(bytes) {
2134 return Ok(existing.clone());
2135 }
2136 self.mark_gc_check_needed();
2137 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2138 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2139 self.global_mut()
2140 .interned_lt
2141 .insert(bytes.to_vec().into_boxed_slice(), new_ref.clone());
2142 Ok(new_ref)
2143 } else {
2144 self.mark_gc_check_needed();
2145 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2146 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2147 Ok(new_ref)
2148 }
2149 }
2150
2151 #[inline(always)]
2153 pub fn top_idx(&self) -> StackIdx {
2154 self.top
2155 }
2156}
2157
2158impl LuaState {
2167 #[inline(always)]
2168 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2169 let i: StackIdx = idx.into().0;
2170 match self.stack.get(i.0 as usize) {
2171 Some(slot) => slot.val.clone(),
2172 None => LuaValue::Nil,
2173 }
2174 }
2175 #[inline(always)]
2176 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2177 let i: StackIdx = idx.into().0;
2178 self.stack[i.0 as usize].val = v;
2179 }
2180
2181 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2187 if end.0 <= start.0 {
2188 return;
2189 }
2190 let end_u = end.0 as usize;
2191 if end_u > self.stack.len() {
2192 self.stack.resize_with(end_u, StackValue::default);
2193 }
2194 for i in start.0..end.0 {
2195 self.stack[i as usize].val = LuaValue::Nil;
2196 self.stack[i as usize].tbc_delta = 0;
2197 }
2198 }
2199 #[inline(always)]
2207 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2208 let i: StackIdx = idx.into().0;
2209 match self.stack.get(i.0 as usize) {
2210 Some(slot) => match &slot.val {
2211 LuaValue::Int(v) => Some(*v),
2212 _ => None,
2213 },
2214 None => None,
2215 }
2216 }
2217 #[inline(always)]
2224 pub fn get_int_pair_at(
2225 &self,
2226 rb: impl Into<StackIdxConv>,
2227 rc: impl Into<StackIdxConv>,
2228 ) -> Option<(i64, i64)> {
2229 let rb: StackIdx = rb.into().0;
2230 let rc: StackIdx = rc.into().0;
2231 match (
2232 self.stack[rb.0 as usize].val,
2233 self.stack[rc.0 as usize].val,
2234 ) {
2235 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2236 _ => None,
2237 }
2238 }
2239 #[inline(always)]
2244 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2245 let i: StackIdx = idx.into().0;
2246 match self.stack.get(i.0 as usize) {
2247 Some(slot) => match &slot.val {
2248 LuaValue::Float(f) => Some(*f),
2249 LuaValue::Int(v) => Some(*v as f64),
2250 _ => None,
2251 },
2252 None => None,
2253 }
2254 }
2255 #[inline(always)]
2260 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2261 let i: StackIdx = idx.into().0;
2262 match self.stack.get(i.0 as usize) {
2263 Some(slot) => match &slot.val {
2264 LuaValue::Float(f) => Some(*f),
2265 _ => None,
2266 },
2267 None => None,
2268 }
2269 }
2270 #[inline(always)]
2275 pub fn get_num_pair_at(
2276 &self,
2277 rb: impl Into<StackIdxConv>,
2278 rc: impl Into<StackIdxConv>,
2279 ) -> Option<(f64, f64)> {
2280 let rb: StackIdx = rb.into().0;
2281 let rc: StackIdx = rc.into().0;
2282 match (
2283 self.stack[rb.0 as usize].val,
2284 self.stack[rc.0 as usize].val,
2285 ) {
2286 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2287 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2288 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2289 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2290 _ => None,
2291 }
2292 }
2293 #[inline(always)]
2306 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2307 let new_top: StackIdx = idx.into().0;
2308 let new_top_u = new_top.0 as usize;
2309 if new_top_u > self.stack.len() {
2310 self.stack.resize_with(new_top_u, StackValue::default);
2311 }
2312 self.top = new_top;
2313 }
2314 #[inline(always)]
2320 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2321 let new_top: StackIdx = idx.into().0;
2322 self.top = new_top;
2323 }
2324 #[inline(always)]
2327 pub fn dec_top(&mut self) {
2328 if self.top.0 > 0 {
2329 self.top = StackIdx(self.top.0 - 1);
2330 }
2331 }
2332 #[inline(always)]
2333 pub fn pop_n(&mut self, n: usize) {
2334 let cur = self.top.0 as usize;
2335 let new = cur.saturating_sub(n);
2336 self.top = StackIdx(new as u32);
2337 }
2338 #[inline(always)]
2341 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2342 let i: StackIdx = idx.into().0;
2343 match self.stack.get(i.0 as usize) {
2344 Some(slot) => slot.val.clone(),
2345 None => LuaValue::Nil,
2346 }
2347 }
2348 #[inline(always)]
2352 pub fn peek_top(&mut self) -> LuaValue {
2353 if self.top.0 == 0 {
2354 return LuaValue::Nil;
2355 }
2356 self.stack[(self.top.0 - 1) as usize].val.clone()
2357 }
2358 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2363 match self.peek_top() {
2364 LuaValue::Str(s) => s,
2365 _ => panic!("peek_string_at_top: top of stack is not a string"),
2366 }
2367 }
2368 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2371 let i: StackIdx = idx.into().0;
2372 &mut self.stack[i.0 as usize].val
2373 }
2374 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2377 let i: StackIdx = idx.into().0;
2378 let slot = i.0 as usize;
2379 if slot < self.stack.len() {
2380 self.stack[slot].val = LuaValue::Nil;
2381 }
2382 }
2383 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2391 self.stack.resize_with(size, StackValue::default);
2392 Ok(())
2393 }
2394 pub fn stack_available(&mut self) -> usize {
2395 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2396 }
2397 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2398 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2399 if free <= n {
2400 self.grow_stack(n, true)?;
2401 }
2402 Ok(())
2403 }
2404 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2413 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2414 }
2415
2416 #[inline(always)]
2417 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo { &self.call_info[idx.as_usize()] }
2418 #[inline(always)]
2419 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo { &mut self.call_info[idx.as_usize()] }
2420 #[inline(always)]
2421 pub fn current_call_info(&self) -> &CallInfo { &self.call_info[self.ci.as_usize()] }
2422 #[inline(always)]
2423 pub fn current_call_info_mut(&mut self) -> &mut CallInfo { let i = self.ci.as_usize(); &mut self.call_info[i] }
2424 #[inline(always)]
2425 pub fn current_ci_idx(&self) -> CallInfoIdx { self.ci }
2426 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> { &mut self.call_info }
2427 #[inline(always)]
2428 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2429 match self.call_info[self.ci.as_usize()].next {
2430 Some(idx) => Ok(idx),
2431 None => Ok(extend_ci(self)),
2432 }
2433 }
2434 #[inline(always)]
2435 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2436 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2437 self.call_info[idx.as_usize()]
2438 .previous
2439 .map(|p| &self.call_info[p.as_usize()])
2440 }
2441 #[inline(always)]
2442 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool { idx.as_usize() == 0 }
2443 #[inline(always)]
2444 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool { idx == self.ci }
2445 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2446 let next = self.call_info[idx.as_usize()]
2447 .next
2448 .expect("ci_next_func: no next CallInfo");
2449 self.call_info[next.as_usize()].func
2450 }
2451 #[inline(always)]
2452 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].top }
2453 #[inline(always)]
2454 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2455 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2456 trap
2457 } else {
2458 false
2459 }
2460 }
2461 #[inline(always)]
2462 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 { self.call_info[idx.as_usize()].saved_pc() }
2463 #[inline(always)]
2464 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2465 self.call_info[idx.as_usize()].set_saved_pc(pc);
2466 }
2467 #[inline(always)]
2468 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2469 self.ci = self.call_info[idx.as_usize()]
2470 .previous
2471 .expect("set_ci_previous: returning frame has no previous CallInfo");
2472 }
2473 #[inline(always)]
2474 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2475 #[inline(always)]
2476 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2477 let ci = &mut self.call_info[idx.as_usize()];
2478 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2479 }
2480 #[inline(always)]
2481 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].func + 1 }
2482 #[inline(always)]
2483 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2484 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2485 }
2486 #[inline(always)]
2487 pub fn ci_lua_closure(&self, idx: CallInfoIdx) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2488 let func_idx = self.call_info[idx.as_usize()].func;
2489 match self.get_at(func_idx) {
2490 LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl)) => Some(cl),
2491 _ => None,
2492 }
2493 }
2494 #[inline(always)]
2495 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2496 self.call_info[idx.as_usize()].nextra_args()
2497 }
2498 #[inline(always)]
2499 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2500 self.call_info[idx.as_usize()].u2.value
2501 }
2502 #[inline(always)]
2503 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2504 self.call_info[idx.as_usize()].u2.value = n;
2505 }
2506 #[inline(always)]
2507 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 { self.call_info[idx.as_usize()].nresults as i32 }
2508 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2509 let pc = self.call_info[idx.as_usize()].saved_pc();
2510 let cl = self.ci_lua_closure(idx)
2511 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2512 cl.proto.code[(pc - 1) as usize]
2513 }
2514 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2515 let pc = self.call_info[idx.as_usize()].saved_pc();
2516 let cl = self.ci_lua_closure(idx)
2517 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2518 cl.proto.code[(pc - 2) as usize]
2519 }
2520 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2521 let pc = self.call_info[idx.as_usize()].saved_pc();
2522 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2523 }
2524 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2525 let pc = self.call_info[idx.as_usize()].saved_pc();
2526 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2527 }
2528 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2529 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2530 }
2531 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2532 self.call_info[idx.as_usize()].u2.value
2533 }
2534 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2535 self.call_info[idx.as_usize()].u2.value
2536 }
2537 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2538 self.call_info[idx.as_usize()].u2.value
2539 }
2540 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2541 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2542 match self.ci_lua_closure(idx) {
2543 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2544 None => (false, nextraargs, 0),
2545 }
2546 }
2547 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2548 self.ci_lua_closure(idx)
2549 .map(|cl| cl.proto.numparams)
2550 .unwrap_or(0)
2551 }
2552 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2553 self.call_info[idx.as_usize()].u2.value = n;
2554 }
2555 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2556 self.call_info[idx.as_usize()].u2.value = n;
2557 }
2558 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2559 let ci = &mut self.call_info[idx.as_usize()];
2560 ci.u2.ftransfer = ftransfer;
2561 ci.u2.ntransfer = ntransfer;
2562 }
2563 pub fn shrink_ci(&mut self) { shrink_ci(self) }
2564 pub fn check_c_stack(&mut self) -> Result<(), LuaError> { check_c_stack(self) }
2565
2566 pub fn status(&mut self) -> LuaStatus { LuaStatus::from_raw(self.status as i32) }
2567 pub fn errfunc(&mut self) -> isize { self.errfunc }
2568 pub fn old_pc(&mut self) -> u32 { self.oldpc }
2569 pub fn set_old_pc(&mut self, pc: u32) { self.oldpc = pc; }
2570 pub fn set_oldpc(&mut self, pc: u32) { self.oldpc = pc; }
2571 pub fn _hook_call_noargs(&mut self) {}
2572 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2573 self.hook.as_ref()
2574 }
2575 pub fn has_hook(&mut self) -> bool { self.hook.is_some() }
2576 pub fn hook_count(&mut self) -> i32 { self.hookcount }
2577 pub fn set_hook_count(&mut self, n: i32) { self.hookcount = n; }
2578 pub fn hook_mask(&self) -> u8 { self.hookmask }
2579 pub fn set_hook_mask(&mut self, m: u8) { self.hookmask = m; }
2580 pub fn base_hook_count(&self) -> i32 { self.basehookcount }
2581 pub fn set_base_hook_count(&mut self, n: i32) { self.basehookcount = n; }
2582 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2583 self.hook = h;
2584 }
2585 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2586 crate::do_::hook(self, event, line, 0, 0)
2587 }
2588
2589 pub fn registry_value(&self) -> LuaValue { self.global().l_registry.clone() }
2590 pub fn registry_get(&self, key: usize) -> LuaValue {
2591 let reg = self.global().l_registry.clone();
2592 match reg {
2593 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2594 _ => LuaValue::Nil,
2595 }
2596 }
2597
2598 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> { self.intern_or_create_str(bytes) }
2599
2600 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2612 self.mark_gc_check_needed();
2613 GcRef::new(LuaProto::placeholder())
2614 }
2615
2616 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2618 self.mark_gc_check_needed();
2619 let mut upvals = Vec::with_capacity(nupvals);
2620 for _ in 0..nupvals {
2621 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
2622 }
2623 GcRef::new(LuaClosureLua { proto, upvals })
2624 }
2625
2626 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
2628 self.mark_gc_check_needed();
2629 GcRef::new(UpVal::closed(v))
2630 }
2631
2632 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
2634 self.mark_gc_check_needed();
2635 GcRef::new(UpVal::open(thread_id, level))
2636 }
2637 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2644 self.intern_str(bytes)
2645 }
2646 pub fn new_userdata(&mut self, _size: usize, _nuvalue: usize) -> Result<GcRef<LuaUserData>, LuaError> {
2647 Err(LuaError::runtime(format_args!("new_userdata not implemented in this Phase-B build; use new_userdata_typed instead")))
2648 }
2649 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
2650 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
2651 }
2652 pub fn push_closure(
2653 &mut self,
2654 proto_idx: usize,
2655 ci: CallInfoIdx,
2656 base: StackIdx,
2657 ra: StackIdx,
2658 ) -> Result<(), LuaError> {
2659 let parent_cl = self.ci_lua_closure(ci).expect(
2660 "push_closure: current frame is not a Lua closure",
2661 );
2662 let child_proto = parent_cl.proto.p[proto_idx].clone();
2663 let nup = child_proto.upvalues.len();
2664 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
2665 for i in 0..nup {
2666 let desc = &child_proto.upvalues[i];
2667 let uv = if desc.instack {
2668 let level = base + desc.idx as i32;
2669 crate::func::find_upval(self, level)
2670 } else {
2671 parent_cl.upval(desc.idx as usize)
2672 };
2673 upvals.push(std::cell::Cell::new(uv));
2674 }
2675 let cache_enabled = matches!(
2679 self.global().lua_version,
2680 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
2681 );
2682 if cache_enabled {
2683 if let Some(cached) = child_proto.cache.borrow().as_ref() {
2684 if cached.upvals.len() == nup
2685 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
2686 {
2687 let reused = cached.clone();
2688 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
2689 return Ok(());
2690 }
2691 }
2692 }
2693 self.mark_gc_check_needed();
2696 let new_cl = GcRef::new(LuaClosureLua {
2697 proto: child_proto.clone(),
2698 upvals,
2699 });
2700 if cache_enabled {
2701 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
2702 }
2703 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
2704 Ok(())
2705 }
2706 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
2707 crate::func::new_tbc_upval(self, idx)
2708 }
2709
2710 #[inline(always)]
2731 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
2732 let uv = cl.upval(n);
2733 let (thread_id, idx) = match uv.try_open_payload() {
2734 Some(p) => p,
2735 None => return *uv.closed_value(),
2736 };
2737 let current = self.cached_thread_id;
2738 let tid = thread_id as u64;
2739 if tid == current {
2740 return self.stack[idx.0 as usize].val;
2741 }
2742 self.upvalue_get_cross_thread(tid, idx)
2743 }
2744
2745 #[cold]
2746 #[inline(never)]
2747 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
2748 let entry_rc = {
2749 let g = self.global();
2750 g.threads.get(&tid).map(|e| e.state.clone())
2751 };
2752 if let Some(rc) = entry_rc {
2753 if let Ok(home_state) = rc.try_borrow() {
2754 return home_state.get_at(idx);
2755 }
2756 }
2757 let g = self.global();
2758 match g.cross_thread_upvals.get(&(tid, idx)) {
2759 Some(v) => *v,
2760 None => LuaValue::Nil,
2761 }
2762 }
2763 #[inline(always)]
2772 pub fn upvalue_set(&mut self, cl: &GcRef<LuaClosureLua>, n: usize, val: LuaValue) -> Result<(), LuaError> {
2773 let uv = cl.upval(n);
2774 match uv.try_open_payload() {
2775 Some((thread_id, idx)) => {
2776 let tid = thread_id as u64;
2777 let current = self.cached_thread_id;
2778 if tid == current {
2779 self.stack[idx.0 as usize].val = val;
2780 } else {
2781 self.upvalue_set_cross_thread(tid, idx, val)?;
2782 }
2783 }
2784 None => {
2785 uv.set_closed_value(val);
2786 }
2787 }
2788 self.gc_barrier_upval(&uv, &val);
2789 Ok(())
2790 }
2791
2792 #[cold]
2793 #[inline(never)]
2794 fn upvalue_set_cross_thread(
2795 &mut self,
2796 tid: u64,
2797 idx: StackIdx,
2798 val: LuaValue,
2799 ) -> Result<(), LuaError> {
2800 let entry_rc = {
2801 let g = self.global();
2802 g.threads.get(&tid).map(|e| e.state.clone())
2803 };
2804 if let Some(rc) = entry_rc {
2805 if let Ok(mut home_state) = rc.try_borrow_mut() {
2806 home_state.set_at(idx, val);
2807 return Ok(());
2808 }
2809 }
2810 let mut g = self.global_mut();
2811 g.cross_thread_upvals.insert((tid, idx), val);
2812 Ok(())
2813 }
2814
2815 pub fn protected_call_raw(&mut self, func: StackIdx, nresults: i32, errfunc: StackIdx) -> Result<(), LuaError> {
2816 let ef = errfunc.0 as isize;
2817 let status = crate::do_::pcall(
2818 self,
2819 |s| s.call_no_yield(func, nresults),
2820 func,
2821 ef,
2822 );
2823 match status {
2824 LuaStatus::Ok => Ok(()),
2825 LuaStatus::ErrSyntax => {
2826 let err_val = self.get_at(func);
2827 self.set_top(func);
2828 Err(LuaError::Syntax(err_val))
2829 }
2830 LuaStatus::Yield => {
2831 self.set_top(func);
2832 Err(LuaError::Yield)
2833 }
2834 _ => {
2835 let err_val = self.get_at(func);
2836 self.set_top(func);
2837 Err(LuaError::Runtime(err_val))
2838 }
2839 }
2840 }
2841 pub fn protected_parser(&mut self, z: crate::zio::ZIO, name: &[u8], mode: Option<&[u8]>) -> LuaStatus {
2842 crate::do_::protected_parser(self, z, name, mode)
2843 }
2844 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2845 crate::do_::call(self, func, nresults)
2846 }
2847 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2848 crate::do_::callnoyield(self, func, nresults)
2849 }
2850 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2851 crate::do_::callnoyield(self, func, nresults)
2852 }
2853 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2854 crate::do_::call(self, func, nresults)
2855 }
2856 #[inline(always)]
2857 pub fn precall(&mut self, func: StackIdx, nresults: i32) -> Result<Option<CallInfoIdx>, LuaError> {
2858 crate::do_::precall(self, func, nresults)
2859 }
2860 #[inline(always)]
2861 pub fn pretailcall(
2862 &mut self,
2863 ci: CallInfoIdx,
2864 func: StackIdx,
2865 narg1: i32,
2866 delta: i32,
2867 ) -> Result<i32, LuaError> {
2868 crate::do_::pretailcall(self, ci, func, narg1, delta)
2869 }
2870 #[inline(always)]
2871 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
2872 where
2873 <N as TryInto<i32>>::Error: std::fmt::Debug,
2874 {
2875 let n = nres.try_into().expect("poscall: nres out of i32 range");
2876 crate::do_::poscall(self, ci, n)
2877 }
2878 pub fn adjust_results(&mut self, nresults: i32) {
2879 const LUA_MULTRET: i32 = -1;
2880 if nresults <= LUA_MULTRET {
2881 let ci_idx = self.ci.as_usize();
2882 if self.call_info[ci_idx].top.0 < self.top.0 {
2883 self.call_info[ci_idx].top = self.top;
2884 }
2885 }
2886 }
2887 pub fn adjust_varargs(
2888 &mut self,
2889 ci: CallInfoIdx,
2890 nfixparams: i32,
2891 cl: &GcRef<lua_types::closure::LuaLClosure>,
2892 ) -> Result<(), LuaError> {
2893 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
2894 }
2895 pub fn get_varargs(
2896 &mut self,
2897 ci: CallInfoIdx,
2898 ra: StackIdx,
2899 n: i32,
2900 ) -> Result<i32, LuaError> {
2901 crate::tagmethods::get_varargs(self, ci, ra, n)?;
2902 Ok(0)
2903 }
2904
2905 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
2906 crate::func::close_upval(self, level);
2907 Ok(())
2908 }
2909 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
2910 crate::func::close_upval(self, level);
2911 Ok(())
2912 }
2913 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
2914 let base = self.ci_base(ci);
2915 crate::func::close_upval(self, base);
2916 Ok(())
2917 }
2918
2919 pub fn arith_op(&mut self, op: i32, p1: &LuaValue, p2: &LuaValue) -> Result<LuaValue, LuaError> {
2920 let arith_op = match op {
2921 0 => lua_types::arith::ArithOp::Add,
2922 1 => lua_types::arith::ArithOp::Sub,
2923 2 => lua_types::arith::ArithOp::Mul,
2924 3 => lua_types::arith::ArithOp::Mod,
2925 4 => lua_types::arith::ArithOp::Pow,
2926 5 => lua_types::arith::ArithOp::Div,
2927 6 => lua_types::arith::ArithOp::Idiv,
2928 7 => lua_types::arith::ArithOp::Band,
2929 8 => lua_types::arith::ArithOp::Bor,
2930 9 => lua_types::arith::ArithOp::Bxor,
2931 10 => lua_types::arith::ArithOp::Shl,
2932 11 => lua_types::arith::ArithOp::Shr,
2933 12 => lua_types::arith::ArithOp::Unm,
2934 13 => lua_types::arith::ArithOp::Bnot,
2935 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
2936 };
2937 let mut res = LuaValue::Nil;
2938 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
2939 Ok(res)
2940 } else {
2941 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
2942 }
2943 }
2944 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
2945 crate::vm::concat(self, n)
2946 }
2947 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
2948 crate::vm::less_than(self, l, r)
2949 }
2950 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
2951 crate::vm::less_equal(self, l, r)
2952 }
2953 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
2954 crate::vm::equal_obj(None, l, r).unwrap_or(false)
2955 }
2956 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
2957 crate::vm::equal_obj(Some(self), l, r)
2958 }
2959 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
2960 match v {
2961 LuaValue::Table(_) => {
2962 let consult_len_tm =
2965 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
2966 let tm = if consult_len_tm {
2967 let mt = self.table_metatable(v);
2968 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
2969 } else {
2970 LuaValue::Nil
2971 };
2972 if matches!(tm, LuaValue::Nil) {
2973 let n = self.table_length(v)?;
2974 return Ok(LuaValue::Int(n));
2975 }
2976 self.push(LuaValue::Nil);
2977 let slot = StackIdx(self.top.0 - 1);
2978 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
2979 Ok(self.pop())
2980 }
2981 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
2982 other => {
2983 let tm = crate::tagmethods::get_tm_by_obj(self, other, crate::tagmethods::TagMethod::Len);
2984 if matches!(tm, LuaValue::Nil) {
2985 let mut msg = b"attempt to get length of a ".to_vec();
2986 msg.extend_from_slice(&self.obj_type_name(other));
2987 msg.extend_from_slice(b" value");
2988 return Err(crate::debug::prefixed_runtime_pub(self, msg));
2989 }
2990 self.push(LuaValue::Nil);
2991 let slot = StackIdx(self.top.0 - 1);
2992 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
2993 Ok(self.pop())
2994 }
2995 }
2996 }
2997 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
2998 let slot: StackIdx = if idx > 0 {
2999 let ci_func = self.current_call_info().func;
3000 ci_func + idx
3001 } else {
3002 debug_assert!(idx != 0, "invalid index");
3003 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3004 };
3005 let val = self.get_at(slot);
3006 match val {
3007 LuaValue::Str(s) => Ok(s),
3008 LuaValue::Int(_) | LuaValue::Float(_) => {
3009 let s = crate::object::num_to_string(self, &val)?;
3010 self.set_at(slot, LuaValue::Str(s.clone()));
3011 Ok(s)
3012 }
3013 _ => Err(LuaError::type_error(&val, "convert to string")),
3014 }
3015 }
3016 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3017 let val = self.get_at(idx);
3018 match val {
3019 LuaValue::Str(s) => Ok(s),
3020 LuaValue::Int(_) | LuaValue::Float(_) => {
3021 let s = crate::object::num_to_string(self, &val)?;
3022 self.set_at(idx, LuaValue::Str(s.clone()));
3023 Ok(s)
3024 }
3025 _ => Err(LuaError::type_error(&val, "convert to string")),
3026 }
3027 }
3028 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3029 let mut out = LuaValue::Nil;
3030 let sz = crate::object::str2num(s, &mut out);
3031 if sz == 0 { None } else { Some((out, sz)) }
3032 }
3033
3034 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3035 let LuaValue::Table(tbl) = t else { return Ok(None); };
3036 let v = tbl.get(k);
3037 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3038 }
3039 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3040 let LuaValue::Table(tbl) = t else { return Ok(None); };
3041 let v = tbl.get_int(k);
3042 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3043 }
3044 pub fn fast_get_short_str(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3045 let LuaValue::Table(tbl) = t else { return Ok(None); };
3046 let LuaValue::Str(s) = k else { return Ok(None); };
3047 let v = tbl.get_short_str(s);
3048 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3049 }
3050 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3051 let Some(mt) = t else { return LuaValue::Nil; };
3052 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3053 let ename = self.global().tmname[tm as usize].clone();
3054 mt.get_short_str(&ename)
3055 }
3056 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3057 let mt = u.metatable();
3059 self.fast_tm_table(mt.as_ref(), tm)
3060 }
3061
3062 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3063 if let LuaValue::Table(tbl) = t {
3069 if tbl.metatable().is_none() {
3070 return Ok(tbl.get(k));
3071 }
3072 }
3073 if let Some(v) = self.fast_get(t, k)? {
3074 return Ok(v);
3075 }
3076 let res = self.top_idx();
3077 self.push(LuaValue::Nil);
3078 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None)?;
3079 let value = self.get_at(res);
3080 self.pop();
3081 Ok(value)
3082 }
3083 #[inline]
3098 pub fn table_set_with_tm(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3099 if let LuaValue::Table(tbl) = t {
3100 if tbl.metatable().is_none() {
3101 self.gc_barrier_back(t, &v);
3102 return self.table_raw_set(t, k, v);
3103 }
3104 }
3105 if self.fast_get(t, &k)?.is_some() {
3106 self.gc_barrier_back(t, &v);
3107 return self.table_raw_set(t, k, v);
3108 }
3109 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3110 }
3111 #[inline]
3112 pub fn table_raw_set(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3113 let LuaValue::Table(tbl) = t else {
3114 return Err(LuaError::type_error(t, "index"));
3115 };
3116 let tbl = tbl.clone();
3117 tbl.raw_set(self, k, v)
3118 }
3119 #[inline]
3120 pub fn table_array_set(&mut self, t: &LuaValue, idx: usize, v: LuaValue) -> Result<(), LuaError> {
3121 let LuaValue::Table(tbl) = t else {
3122 return Err(LuaError::type_error(t, "index"));
3123 };
3124 let tbl = tbl.clone();
3125 tbl.raw_set_int(self, idx as i64 + 1, v)
3126 }
3127 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3128 let LuaValue::Table(tbl) = t else {
3129 return Err(LuaError::type_error(t, "index"));
3130 };
3131 if n > tbl.array_len() {
3132 tbl.resize(self, n, 0)?;
3133 }
3134 Ok(())
3135 }
3136 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3137 let LuaValue::Table(tbl) = t else {
3138 return Err(LuaError::type_error(t, "get length of"));
3139 };
3140 Ok(tbl.getn() as i64)
3141 }
3142 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3143 match v {
3144 LuaValue::Table(t) => t.metatable(),
3145 LuaValue::UserData(u) => u.metatable(),
3146 other => {
3147 let idx = other.base_type() as usize;
3148 self.global().mt[idx].clone()
3149 }
3150 }
3151 }
3152 pub fn table_resize(&mut self, t: &GcRef<LuaTable>, na: usize, nh: usize) -> Result<(), LuaError> {
3153 self.mark_gc_check_needed();
3154 t.resize(self, na, nh)
3155 }
3156 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3157 let mut i: i64 = 1;
3165 loop {
3166 let v = t.get_int(i);
3167 if matches!(v, LuaValue::Nil) {
3168 return i - 1;
3169 }
3170 i += 1;
3171 }
3172 }
3173
3174 pub fn try_bin_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3175 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3176 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3177 }
3178 pub fn try_bin_i_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, imm: i64, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3179 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3180 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3181 }
3182 pub fn try_bin_assoc_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3183 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3184 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3185 }
3186 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3187 crate::tagmethods::try_concat_tm(self)
3188 }
3189 pub fn call_tm(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, p3: &LuaValue) -> Result<(), LuaError> {
3190 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3191 }
3192 pub fn call_tm_res(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, res: StackIdx) -> Result<(), LuaError> {
3193 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3194 }
3195 pub fn call_tm_res_bool(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue) -> Result<bool, LuaError> {
3196 let res = self.top_idx();
3197 self.push(LuaValue::Nil);
3198 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3199 let result = self.get_at(res).clone();
3200 self.pop();
3201 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3202 }
3203 pub fn call_order_tm(&mut self, p1: &LuaValue, p2: &LuaValue, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3204 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3205 crate::tagmethods::call_order_tm(self, p1, p2, event)
3206 }
3207 pub fn call_order_i_tm(&mut self, p1: &LuaValue, v2: i64, flip: bool, isfloat: bool, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3208 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3209 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3210 }
3211
3212 #[inline(always)]
3213 pub fn proto_code(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, pc: u32) -> lua_types::opcode::Instruction {
3214 cl.proto.code[pc as usize]
3215 }
3216 #[inline(always)]
3217 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3218 cl.proto.k[idx].clone()
3219 }
3220 #[inline(always)]
3226 pub fn proto_const_int(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<i64> {
3227 match &cl.proto.k[idx] {
3228 LuaValue::Int(v) => Some(*v),
3229 _ => None,
3230 }
3231 }
3232 #[inline(always)]
3236 pub fn proto_const_num(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<f64> {
3237 match &cl.proto.k[idx] {
3238 LuaValue::Float(f) => Some(*f),
3239 LuaValue::Int(v) => Some(*v as f64),
3240 _ => None,
3241 }
3242 }
3243 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3244 let cl = self.ci_lua_closure(ci)
3245 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3246 cl.proto.code[pc as usize]
3247 }
3248 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3253 Ok(crate::debug::trace_call(self)? != 0)
3254 }
3255 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3258 Ok(crate::debug::trace_exec(self, pc)? != 0)
3259 }
3260 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3261 crate::do_::hookcall(self, idx)
3262 }
3263 #[inline(always)]
3264 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3265 let g = self.global();
3266 if !g.is_gc_running() {
3267 return None;
3268 }
3269 let should_collect = g.heap.would_collect();
3270 let has_finalizers = !g.to_be_finalized.is_empty();
3271 if should_collect || has_finalizers {
3272 Some((should_collect, has_finalizers))
3273 } else {
3274 None
3275 }
3276 }
3277
3278 #[inline(always)]
3279 fn should_check_gc(&mut self) -> bool {
3280 if self.gc_check_needed {
3281 return true;
3282 }
3283 if !self.global().to_be_finalized.is_empty() {
3284 self.gc_check_needed = true;
3285 return true;
3286 }
3287 false
3288 }
3289
3290 #[inline(always)]
3291 pub(crate) fn mark_gc_check_needed(&mut self) {
3292 self.gc_check_needed = true;
3293 }
3294
3295 #[inline(always)]
3296 pub fn gc_check_step(&mut self) {
3297 if !self.allowhook {
3298 return;
3299 }
3300 if !self.should_check_gc() {
3301 return;
3302 }
3303 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3304 self.gc_check_needed = false;
3305 return;
3306 };
3307 if should_collect || has_finalizers {
3308 if should_collect {
3309 self.gc().check_step();
3310 }
3311 crate::api::run_pending_finalizers(self);
3312 self.gc_check_needed = true;
3313 }
3314 let should_keep_checking = {
3315 let g = self.global();
3316 g.heap.would_collect() || !g.to_be_finalized.is_empty()
3317 };
3318 self.gc_check_needed = should_keep_checking;
3319 }
3320 #[inline(always)]
3321 pub fn gc_cond_step(&mut self) {
3322 if !self.allowhook {
3323 return;
3324 }
3325 if !self.should_check_gc() {
3326 return;
3327 }
3328 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3329 self.gc_check_needed = false;
3330 return;
3331 };
3332 if should_collect || has_finalizers {
3333 if should_collect {
3334 self.gc().check_step();
3335 }
3336 crate::api::run_pending_finalizers(self);
3337 self.gc_check_needed = true;
3338 }
3339 let should_keep_checking = {
3340 let g = self.global();
3341 g.heap.would_collect() || !g.to_be_finalized.is_empty()
3342 };
3343 self.gc_check_needed = should_keep_checking;
3344 }
3345 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3346 self.gc().barrier_back(t, v);
3347 }
3348 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3349 self.gc().barrier(uv, v);
3350 }
3351 pub fn is_main_thread(&mut self) -> bool {
3357 let g = self.global();
3358 g.current_thread_id == g.main_thread_id
3359 }
3360 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3361 match v {
3362 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3363 LuaValue::Table(t) => {
3364 if let Some(mt) = t.metatable() {
3365 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3366 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3367 }
3368 }
3369 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3370 }
3371 LuaValue::UserData(u) => {
3372 if let Some(mt) = u.metatable() {
3373 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3374 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3375 }
3376 }
3377 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3378 }
3379 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3380 }
3381 }
3382
3383 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3384 crate::tagmethods::obj_type_name(self, v)
3385 }
3386 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) { warning(self, _msg, _to_cont) }
3387}
3388
3389pub struct GcHandle<'a> {
3395 _state: &'a mut LuaState,
3396}
3397
3398struct CollectRoots<'a> {
3405 global: &'a GlobalState,
3406 thread: &'a LuaState,
3407}
3408
3409#[derive(Clone, Copy)]
3410enum HeapCollectMode {
3411 Full,
3412 Step,
3413 Minor,
3414}
3415
3416impl<'a> lua_gc::Trace for CollectRoots<'a> {
3417 fn trace(&self, m: &mut lua_gc::Marker) {
3418 self.global.trace(m);
3419 self.thread.trace(m);
3420 }
3421}
3422
3423#[derive(Clone, Copy)]
3424enum BarrierKind {
3425 Forward,
3426 Backward,
3427}
3428
3429fn barrier_lua_value<P>(
3430 heap: &lua_gc::Heap,
3431 parent: GcRef<P>,
3432 child: &LuaValue,
3433 generational: bool,
3434 kind: BarrierKind,
3435)
3436where
3437 P: lua_gc::Trace + 'static,
3438{
3439 if generational && matches!(kind, BarrierKind::Backward) {
3440 heap.generational_backward_barrier(parent.0);
3441 }
3442 match child {
3443 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3444 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3445 LuaValue::Function(LuaClosure::Lua(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3446 LuaValue::Function(LuaClosure::C(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3447 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3448 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3449 LuaValue::Nil
3450 | LuaValue::Bool(_)
3451 | LuaValue::Int(_)
3452 | LuaValue::Float(_)
3453 | LuaValue::LightUserData(_)
3454 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3455 }
3456}
3457
3458fn barrier_gc_child<P, C>(
3459 heap: &lua_gc::Heap,
3460 parent: GcRef<P>,
3461 child: GcRef<C>,
3462 generational: bool,
3463 kind: BarrierKind,
3464)
3465where
3466 P: lua_gc::Trace + 'static,
3467 C: lua_gc::Trace + 'static,
3468{
3469 if generational && matches!(kind, BarrierKind::Forward) {
3470 heap.generational_forward_barrier(parent.0, child.0);
3471 } else {
3472 heap.barrier(parent.0, child.0);
3473 }
3474}
3475
3476fn barrier_child_any<P>(
3477 heap: &lua_gc::Heap,
3478 parent: GcRef<P>,
3479 child: &dyn std::any::Any,
3480 generational: bool,
3481 kind: BarrierKind,
3482)
3483where
3484 P: lua_gc::Trace + 'static,
3485{
3486 if let Some(v) = child.downcast_ref::<LuaValue>() {
3487 barrier_lua_value(heap, parent, v, generational, kind);
3488 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
3489 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3490 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
3491 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3492 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
3493 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3494 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
3495 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3496 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
3497 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3498 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3499 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3500 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
3501 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3502 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
3503 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3504 }
3505}
3506
3507fn barrier_any(
3508 heap: &lua_gc::Heap,
3509 parent: &dyn std::any::Any,
3510 child: &dyn std::any::Any,
3511 generational: bool,
3512 kind: BarrierKind,
3513) {
3514 if let Some(v) = parent.downcast_ref::<LuaValue>() {
3515 match v {
3516 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
3517 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
3518 LuaValue::Function(LuaClosure::Lua(p)) => barrier_child_any(heap, *p, child, generational, kind),
3519 LuaValue::Function(LuaClosure::C(p)) => barrier_child_any(heap, *p, child, generational, kind),
3520 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
3521 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
3522 LuaValue::Nil
3523 | LuaValue::Bool(_)
3524 | LuaValue::Int(_)
3525 | LuaValue::Float(_)
3526 | LuaValue::LightUserData(_)
3527 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3528 }
3529 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
3530 barrier_child_any(heap, p.clone(), child, generational, kind);
3531 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
3532 barrier_child_any(heap, p.clone(), child, generational, kind);
3533 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
3534 barrier_child_any(heap, p.clone(), child, generational, kind);
3535 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
3536 barrier_child_any(heap, p.clone(), child, generational, kind);
3537 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
3538 barrier_child_any(heap, p.clone(), child, generational, kind);
3539 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3540 barrier_child_any(heap, p.clone(), child, generational, kind);
3541 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
3542 barrier_child_any(heap, p.clone(), child, generational, kind);
3543 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
3544 barrier_child_any(heap, p.clone(), child, generational, kind);
3545 }
3546}
3547
3548fn trace_reachable_threads(
3549 global: &GlobalState,
3550 _current_thread_id: u64,
3551 marker: &mut lua_gc::Marker,
3552) {
3553 use lua_gc::Trace;
3554
3555 loop {
3556 let visited_before = marker.visited_count();
3557 for (id, entry) in global.threads.iter() {
3558 if thread_entry_marked_alive(marker, *id, entry) {
3559 if let Ok(thread) = entry.state.try_borrow() {
3560 thread.trace(marker);
3561 }
3562 }
3563 }
3564 marker.drain_gray_queue();
3565 if marker.visited_count() == visited_before {
3566 break;
3567 }
3568 }
3569}
3570
3571fn thread_entry_marked_alive(
3572 marker: &lua_gc::Marker,
3573 id: u64,
3574 entry: &ThreadRegistryEntry,
3575) -> bool {
3576 marker.is_visited(entry.value.identity()) && entry.value.id == id
3577}
3578
3579fn close_open_upvalues_for_unreachable_threads(
3580 global: &GlobalState,
3581 marker: &mut lua_gc::Marker,
3582) {
3583 use lua_gc::Trace;
3584
3585 let mut closed_values = Vec::<LuaValue>::new();
3586 for (id, entry) in global.threads.iter() {
3587 if entry.value.id != *id {
3588 continue;
3589 }
3590 if thread_entry_marked_alive(marker, *id, entry) {
3591 continue;
3592 }
3593 let Ok(thread) = entry.state.try_borrow() else {
3594 continue;
3595 };
3596 for uv in thread.openupval.iter() {
3597 if !marker.is_visited(uv.identity()) {
3598 continue;
3599 }
3600 let Some((thread_id, idx)) = uv.try_open_payload() else {
3601 continue;
3602 };
3603 if thread_id as u64 != *id {
3604 continue;
3605 }
3606 let value = thread.get_at(idx);
3607 uv.close_with(value.clone());
3608 closed_values.push(value);
3609 }
3610 }
3611 for value in closed_values {
3612 value.trace(marker);
3613 }
3614 marker.drain_gray_queue();
3615}
3616
3617fn record_live_interned_strings(
3618 global: &GlobalState,
3619 marker: &lua_gc::Marker,
3620 live_ids: &std::cell::RefCell<std::collections::HashSet<usize>>,
3621) {
3622 let mut live = live_ids.borrow_mut();
3623 for s in global.interned_lt.values() {
3624 let id = s.identity();
3625 if marker.is_visited(id) {
3626 live.insert(id);
3627 }
3628 }
3629}
3630
3631fn retain_live_interned_strings(
3632 global: &mut GlobalState,
3633 live_ids: &std::collections::HashSet<usize>,
3634) {
3635 global
3636 .interned_lt
3637 .retain(|_, s| live_ids.contains(&s.identity()));
3638}
3639
3640impl<'a> GcHandle<'a> {
3641 pub fn check_step(&self) {
3648 if !self._state.global().is_gc_running() {
3649 return;
3650 }
3651 if self._state.global().is_gen_mode() {
3652 let should_collect = {
3653 let g = self._state.global();
3654 g.heap.would_collect() || g.gc_debt() > 0
3655 };
3656 if should_collect {
3657 self.generational_step();
3658 }
3659 } else {
3660 self.collect_via_heap(false);
3661 }
3662 }
3663
3664 pub fn full_collect(&self) {
3666 if self._state.global().is_gen_mode() {
3667 self.fullgen();
3668 } else {
3669 self.collect_via_heap(true);
3670 }
3671 }
3672
3673 fn negative_debt(bytes: usize) -> isize {
3674 -(bytes.min(isize::MAX as usize) as isize)
3675 }
3676
3677 fn set_minor_debt(&self) {
3678 let mut g = self._state.global_mut();
3679 let total = g.total_bytes();
3680 let growth = (total / 100).saturating_mul(g.genminormul as usize);
3681 g.heap
3682 .set_threshold_bytes(total.saturating_add(growth.max(1)));
3683 set_debt(&mut *g, Self::negative_debt(growth));
3684 }
3685
3686 fn set_pause_debt(&self) {
3687 let mut g = self._state.global_mut();
3688 let total = g.total_bytes();
3689 let pause = g.gc_pause_param().max(0) as usize;
3690 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
3691 let debt = if threshold > total {
3692 Self::negative_debt(threshold - total)
3693 } else {
3694 0
3695 };
3696 let heap_threshold = if threshold > total {
3697 threshold
3698 } else {
3699 total.saturating_add(1)
3700 };
3701 g.heap.set_threshold_bytes(heap_threshold);
3702 set_debt(&mut *g, debt);
3703 }
3704
3705 fn enter_incremental_mode(&self) {
3706 {
3707 let g = self._state.global();
3708 g.heap.reset_all_ages();
3709 }
3710 let mut g = self._state.global_mut();
3711 g.gckind = GcKind::Incremental as u8;
3712 g.lastatomic = 0;
3713 }
3714
3715 fn enter_generational_mode(&self) -> usize {
3716 self.collect_via_heap_mode(HeapCollectMode::Full);
3717 let numobjs = {
3718 let g = self._state.global();
3719 g.heap.promote_all_to_old();
3720 g.heap.allgc_count()
3721 };
3722 let total = self._state.global().total_bytes();
3723 {
3724 let mut g = self._state.global_mut();
3725 g.gckind = GcKind::Generational as u8;
3726 g.lastatomic = 0;
3727 g.gc_estimate = total;
3728 }
3729 self.set_minor_debt();
3730 numobjs
3731 }
3732
3733 fn fullgen(&self) -> usize {
3734 self.enter_incremental_mode();
3735 self.enter_generational_mode()
3736 }
3737
3738 fn stepgenfull(&self, lastatomic: usize) {
3739 if self._state.global().gckind == GcKind::Generational as u8 {
3740 self.enter_incremental_mode();
3741 }
3742 self.collect_via_heap_mode(HeapCollectMode::Full);
3743 let newatomic = self._state.global().heap.allgc_count().max(1);
3744 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
3745 {
3746 let g = self._state.global();
3747 g.heap.promote_all_to_old();
3748 }
3749 let total = self._state.global().total_bytes();
3750 {
3751 let mut g = self._state.global_mut();
3752 g.gckind = GcKind::Generational as u8;
3753 g.lastatomic = 0;
3754 g.gc_estimate = total;
3755 }
3756 self.set_minor_debt();
3757 } else {
3758 {
3759 let g = self._state.global();
3760 g.heap.reset_all_ages();
3761 }
3762 let total = self._state.global().total_bytes();
3763 {
3764 let mut g = self._state.global_mut();
3765 g.gckind = GcKind::Incremental as u8;
3766 g.lastatomic = newatomic;
3767 g.gc_estimate = total;
3768 }
3769 self.set_pause_debt();
3770 }
3771 }
3772
3773 fn collect_via_heap(&self, force: bool) {
3782 self.collect_via_heap_mode(if force {
3783 HeapCollectMode::Full
3784 } else {
3785 HeapCollectMode::Step
3786 });
3787 }
3788
3789 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
3790 use lua_gc::Trace;
3791 let state_ref: &LuaState = &*self._state;
3792
3793 if matches!(mode, HeapCollectMode::Step) {
3799 let g = state_ref.global.borrow();
3800 if !g.heap.would_collect() {
3801 return;
3802 }
3803 }
3804
3805 let weak_tables_snapshot: Vec<lua_types::gc::GcRef<lua_types::value::LuaTable>> = {
3809 let g = state_ref.global.borrow();
3810 let mut seen = std::collections::HashSet::<usize>::new();
3811 g.weak_tables_registry
3812 .iter()
3813 .filter_map(|w| w.upgrade())
3814 .filter(|t| seen.insert(t.identity()))
3815 .collect()
3816 };
3817
3818 let pending_snapshot: Vec<FinalizerObject> = {
3823 let g = state_ref.global.borrow();
3824 g.pending_finalizers.clone()
3825 };
3826
3827 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
3828 std::cell::RefCell::new(std::collections::HashSet::new());
3829 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
3830 std::cell::RefCell::new(Vec::new());
3831 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
3832 std::cell::RefCell::new(std::collections::HashSet::new());
3833 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
3834 std::cell::RefCell::new(std::collections::HashSet::new());
3835 let collect_ran = std::cell::Cell::new(false);
3836
3837 {
3838 let global = state_ref.global.borrow();
3839 global.heap.unpause();
3840 let roots = CollectRoots { global: &*global, thread: state_ref };
3841 let hook = |marker: &mut lua_gc::Marker| {
3842 collect_ran.set(true);
3843 trace_reachable_threads(&*global, global.current_thread_id, marker);
3844 close_open_upvalues_for_unreachable_threads(&*global, marker);
3845 loop {
3846 let visited_before = marker.visited_count();
3847 for t in &weak_tables_snapshot {
3848 let t_id = t.identity();
3849 if !marker.is_visited(t_id) {
3850 continue;
3851 }
3852 let to_mark = t.ephemeron_values_to_mark(
3853 &|id| marker.is_visited(id),
3854 );
3855 for v in &to_mark {
3856 v.trace(marker);
3857 }
3858 }
3859 marker.drain_gray_queue();
3860 if marker.visited_count() == visited_before {
3861 break;
3862 }
3863 }
3864 for pf in &pending_snapshot {
3865 if !marker.is_visited(pf.identity()) {
3866 pf.mark(marker);
3867 newly_unreachable.borrow_mut().push(pf.clone());
3868 }
3869 }
3870 marker.drain_gray_queue();
3871 loop {
3872 let visited_before = marker.visited_count();
3873 for t in &weak_tables_snapshot {
3874 let t_id = t.identity();
3875 if !marker.is_visited(t_id) {
3876 continue;
3877 }
3878 let to_mark = t.ephemeron_values_to_mark(
3879 &|id| marker.is_visited(id),
3880 );
3881 for v in &to_mark {
3882 v.trace(marker);
3883 }
3884 }
3885 marker.drain_gray_queue();
3886 if marker.visited_count() == visited_before {
3887 break;
3888 }
3889 }
3890 for t in &weak_tables_snapshot {
3891 let id = t.identity();
3892 if marker.is_visited(id) {
3893 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
3894 for v in &to_mark {
3895 v.trace(marker);
3896 }
3897 alive_ids.borrow_mut().insert(id);
3898 }
3899 }
3900 marker.drain_gray_queue();
3901 {
3902 let mut alive = alive_thread_ids.borrow_mut();
3903 for (id, entry) in global.threads.iter() {
3904 if thread_entry_marked_alive(marker, *id, entry) {
3905 alive.insert(*id);
3906 }
3907 }
3908 }
3909 record_live_interned_strings(&*global, marker, &live_interned_ids);
3910 };
3911 match mode {
3912 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
3913 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
3914 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
3915 }
3916 }
3917
3918 if !collect_ran.get() {
3919 return;
3920 }
3921
3922 let alive_set = alive_ids.into_inner();
3926 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
3927 let promote_ids: std::collections::HashSet<usize> =
3928 promote.iter().map(|t| t.identity()).collect();
3929 let alive_thread_ids = alive_thread_ids.into_inner();
3930 let live_interned_ids = live_interned_ids.into_inner();
3931 let mut g = state_ref.global.borrow_mut();
3932 retain_live_interned_strings(&mut *g, &live_interned_ids);
3933 g.weak_tables_registry
3934 .retain(|w| alive_set.contains(&w.identity()));
3935 let main_thread_id = g.main_thread_id;
3936 g.threads.retain(|id, _| alive_thread_ids.contains(id));
3937 g.cross_thread_upvals
3938 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
3939 g.pending_finalizers
3943 .retain(|t| !promote_ids.contains(&t.identity()));
3944 g.to_be_finalized.extend(promote);
3945 }
3946
3947 pub fn generational_step(&self) -> bool {
3949 let (lastatomic, majorbase, majorinc, should_major) = {
3950 let g = self._state.global();
3951 let majorbase = if g.gc_estimate == 0 {
3952 g.total_bytes()
3953 } else {
3954 g.gc_estimate
3955 };
3956 let majormul = g.gc_genmajormul_param().max(0) as usize;
3957 let majorinc = (majorbase / 100).saturating_mul(majormul);
3958 let should_major = g.gc_debt() > 0
3959 && g.total_bytes() > majorbase.saturating_add(majorinc);
3960 (g.lastatomic, majorbase, majorinc, should_major)
3961 };
3962
3963 if lastatomic != 0 {
3964 self.stepgenfull(lastatomic);
3965 debug_assert!(self._state.global().is_gen_mode());
3966 return true;
3967 }
3968
3969 if should_major {
3970 let numobjs = self.fullgen();
3971 let after = self._state.global().total_bytes();
3972 if after < majorbase.saturating_add(majorinc / 2) {
3973 self.set_minor_debt();
3974 } else {
3975 {
3976 let mut g = self._state.global_mut();
3977 g.lastatomic = numobjs.max(1);
3978 }
3979 self.set_pause_debt();
3980 }
3981 } else {
3982 self.collect_via_heap_mode(HeapCollectMode::Minor);
3983 self.set_minor_debt();
3984 self._state.global_mut().gc_estimate = majorbase;
3985 }
3986
3987 debug_assert!(self._state.global().is_gen_mode());
3988 true
3989 }
3990
3991 pub fn step(&self) { }
3993
3994 pub fn incremental_step(&self, work_units: isize) -> bool {
4007 self.incremental_step_to_state(work_units, None)
4008 }
4009
4010 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4015 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4016 self._state.global().heap.gc_state() == target
4017 }
4018
4019 fn incremental_step_to_state(
4020 &self,
4021 work_units: isize,
4022 target: Option<lua_gc::GcState>,
4023 ) -> bool {
4024 use lua_gc::{StepBudget, StepOutcome, Trace};
4025 let state_ref: &LuaState = &*self._state;
4026
4027 let weak_tables_snapshot: Vec<lua_types::gc::GcRef<lua_types::value::LuaTable>> = {
4028 let g = state_ref.global.borrow();
4029 let mut seen = std::collections::HashSet::<usize>::new();
4030 g.weak_tables_registry
4031 .iter()
4032 .filter_map(|w| w.upgrade())
4033 .filter(|t| seen.insert(t.identity()))
4034 .collect()
4035 };
4036
4037 let pending_snapshot: Vec<FinalizerObject> = {
4038 let g = state_ref.global.borrow();
4039 g.pending_finalizers.clone()
4040 };
4041
4042 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4043 std::cell::RefCell::new(std::collections::HashSet::new());
4044 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4045 std::cell::RefCell::new(Vec::new());
4046 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4047 std::cell::RefCell::new(std::collections::HashSet::new());
4048 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4049 std::cell::RefCell::new(std::collections::HashSet::new());
4050 let atomic_ran = std::cell::Cell::new(false);
4051
4052 let outcome = {
4053 let global = state_ref.global.borrow();
4054 global.heap.unpause();
4055 let roots = CollectRoots { global: &*global, thread: state_ref };
4056 let hook = |marker: &mut lua_gc::Marker| {
4057 atomic_ran.set(true);
4058 trace_reachable_threads(&*global, global.current_thread_id, marker);
4059 close_open_upvalues_for_unreachable_threads(&*global, marker);
4060 loop {
4061 let visited_before = marker.visited_count();
4062 for t in &weak_tables_snapshot {
4063 let t_id = t.identity();
4064 if !marker.is_visited(t_id) {
4065 continue;
4066 }
4067 let to_mark = t.ephemeron_values_to_mark(
4068 &|id| marker.is_visited(id),
4069 );
4070 for v in &to_mark {
4071 v.trace(marker);
4072 }
4073 }
4074 marker.drain_gray_queue();
4075 if marker.visited_count() == visited_before {
4076 break;
4077 }
4078 }
4079 for pf in &pending_snapshot {
4080 if !marker.is_visited(pf.identity()) {
4081 pf.mark(marker);
4082 newly_unreachable.borrow_mut().push(pf.clone());
4083 }
4084 }
4085 marker.drain_gray_queue();
4086 loop {
4087 let visited_before = marker.visited_count();
4088 for t in &weak_tables_snapshot {
4089 let t_id = t.identity();
4090 if !marker.is_visited(t_id) {
4091 continue;
4092 }
4093 let to_mark = t.ephemeron_values_to_mark(
4094 &|id| marker.is_visited(id),
4095 );
4096 for v in &to_mark {
4097 v.trace(marker);
4098 }
4099 }
4100 marker.drain_gray_queue();
4101 if marker.visited_count() == visited_before {
4102 break;
4103 }
4104 }
4105 for t in &weak_tables_snapshot {
4106 let id = t.identity();
4107 if marker.is_visited(id) {
4108 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4109 for v in &to_mark {
4110 v.trace(marker);
4111 }
4112 alive_ids.borrow_mut().insert(id);
4113 }
4114 }
4115 marker.drain_gray_queue();
4116 {
4117 let mut alive = alive_thread_ids.borrow_mut();
4118 for (id, entry) in global.threads.iter() {
4119 if thread_entry_marked_alive(marker, *id, entry) {
4120 alive.insert(*id);
4121 }
4122 }
4123 }
4124 record_live_interned_strings(&*global, marker, &live_interned_ids);
4125 };
4126 let budget = StepBudget::from_work(work_units);
4127 if let Some(target) = target {
4128 global.heap.incremental_run_until_state_with_post_mark(
4129 &roots,
4130 target,
4131 work_units,
4132 hook,
4133 )
4134 } else {
4135 global.heap.incremental_step_with_post_mark(&roots, budget, hook)
4136 }
4137 };
4138
4139 if atomic_ran.get() {
4140 let alive_set = alive_ids.into_inner();
4141 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4142 let promote_ids: std::collections::HashSet<usize> =
4143 promote.iter().map(|t| t.identity()).collect();
4144 let alive_thread_ids = alive_thread_ids.into_inner();
4145 let live_interned_ids = live_interned_ids.into_inner();
4146 let mut g = state_ref.global.borrow_mut();
4147 retain_live_interned_strings(&mut *g, &live_interned_ids);
4148 g.weak_tables_registry
4149 .retain(|w| alive_set.contains(&w.identity()));
4150 let main_thread_id = g.main_thread_id;
4151 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4152 g.cross_thread_upvals
4153 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4154 g.pending_finalizers
4155 .retain(|t| !promote_ids.contains(&t.identity()));
4156 g.to_be_finalized.extend(promote);
4157 }
4158
4159 matches!(outcome, StepOutcome::Paused)
4160 }
4161
4162 pub fn prune_weak_tables_mark_only(&self) {
4169 use lua_gc::Trace;
4170 let state_ref: &LuaState = &*self._state;
4171
4172 let weak_tables_snapshot: Vec<lua_types::gc::GcRef<lua_types::value::LuaTable>> = {
4173 let g = state_ref.global.borrow();
4174 let mut seen = std::collections::HashSet::<usize>::new();
4175 g.weak_tables_registry
4176 .iter()
4177 .filter_map(|w| w.upgrade())
4178 .filter(|t| seen.insert(t.identity()))
4179 .collect()
4180 };
4181
4182 let live_interned_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4183 std::cell::RefCell::new(std::collections::HashSet::new());
4184
4185 {
4186 let global = state_ref.global.borrow();
4187 global.heap.unpause();
4188 let roots = CollectRoots { global: &*global, thread: state_ref };
4189 let hook = |marker: &mut lua_gc::Marker| {
4190 trace_reachable_threads(&*global, global.current_thread_id, marker);
4191 loop {
4192 let visited_before = marker.visited_count();
4193 for t in &weak_tables_snapshot {
4194 let t_id = t.identity();
4195 if !marker.is_visited(t_id) {
4196 continue;
4197 }
4198 let to_mark = t.ephemeron_values_to_mark(
4199 &|id| marker.is_visited(id),
4200 );
4201 for v in &to_mark {
4202 v.trace(marker);
4203 }
4204 }
4205 marker.drain_gray_queue();
4206 if marker.visited_count() == visited_before {
4207 break;
4208 }
4209 }
4210 for t in &weak_tables_snapshot {
4211 if marker.is_visited(t.identity()) {
4212 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4213 for v in &to_mark {
4214 v.trace(marker);
4215 }
4216 }
4217 }
4218 marker.drain_gray_queue();
4219 record_live_interned_strings(&*global, marker, &live_interned_ids);
4220 };
4221 global.heap.mark_only_with_post_mark(&roots, hook);
4222 }
4223
4224 let live_interned_ids = live_interned_ids.into_inner();
4225 let mut g = state_ref.global.borrow_mut();
4226 retain_live_interned_strings(&mut *g, &live_interned_ids);
4227 }
4228
4229 pub fn change_mode(&self, mode: GcKind) {
4231 let old = self._state.global().gckind;
4232 if old == mode as u8 {
4233 self._state.global_mut().lastatomic = 0;
4234 return;
4235 }
4236 match mode {
4237 GcKind::Generational => {
4238 self.enter_generational_mode();
4239 }
4240 GcKind::Incremental => {
4241 self.enter_incremental_mode();
4242 }
4243 }
4244 }
4245
4246 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4248
4249 pub fn free_all_objects(&self) {
4253 }
4255
4256 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4260 let g = self._state.global();
4261 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4262 }
4263
4264 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4268 let g = self._state.global();
4269 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4270 }
4271
4272 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4276 let g = self._state.global();
4277 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4278 }
4279
4280 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4283 let g = self._state.global();
4284 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4285 }
4286}
4287
4288fn make_seed() -> u32 {
4297 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4298 {
4299 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
4300 }
4301
4302 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
4303 {
4304 use std::time::{SystemTime, UNIX_EPOCH};
4305 let t = SystemTime::now()
4306 .duration_since(UNIX_EPOCH)
4307 .map(|d| d.as_secs() as u32)
4308 .unwrap_or(0);
4309
4310 crate::string::hash_bytes(&t.to_le_bytes(), t)
4318 }
4319}
4320
4321pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
4336 let tb = g.total_bytes() as isize;
4337 debug_assert!(tb > 0);
4338 if debt < tb.saturating_sub(isize::MAX) {
4340 debt = tb - isize::MAX;
4341 }
4342 g.totalbytes = tb - debt;
4343 g.gc_debt = debt;
4344}
4345
4346pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
4356 let _ = (_state, _limit);
4357 LUAI_MAXCCALLS as i32
4358}
4359
4360pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
4377 debug_assert!(
4378 state.call_info[state.ci.0 as usize].next.is_none(),
4379 "extend_ci: current ci already has a cached next frame"
4380 );
4381
4382 let current_idx = state.ci;
4383 let new_idx = CallInfoIdx(state.call_info.len() as u32);
4385
4386 state.call_info.push(CallInfo {
4387 previous: Some(current_idx),
4388 next: None,
4389 u: CallInfoFrame::lua_default(),
4390 ..CallInfo::default()
4391 });
4392
4393 state.call_info[current_idx.0 as usize].next = Some(new_idx);
4394
4395 state.nci += 1;
4396
4397 new_idx
4398}
4399
4400fn free_ci(state: &mut LuaState) {
4421 let ci_idx = state.ci.0 as usize;
4422
4423 let mut next_opt = state.call_info[ci_idx].next.take();
4424
4425 while let Some(idx) = next_opt {
4426 next_opt = state.call_info[idx.0 as usize].next;
4427 state.nci = state.nci.saturating_sub(1);
4428 }
4429
4430 state.call_info.truncate(ci_idx + 1);
4433}
4434
4435pub(crate) fn shrink_ci(state: &mut LuaState) {
4462 let ci_idx = state.ci.0 as usize;
4463
4464 if state.call_info[ci_idx].next.is_none() {
4465 return;
4466 }
4467
4468 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
4469 if free_count <= 1 {
4470 return;
4471 }
4472
4473 let keep = free_count / 2;
4477 let removed = free_count - keep;
4478 let new_len = ci_idx + 1 + keep;
4479 state.call_info.truncate(new_len);
4480 state.nci = state.nci.saturating_sub(removed as u32);
4481
4482 if let Some(last) = state.call_info.last_mut() {
4484 last.next = None;
4485 }
4486}
4487
4488pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4500 if state.c_calls() == LUAI_MAXCCALLS {
4503 return Err(LuaError::runtime(format_args!("C stack overflow")));
4504 }
4505 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
4507 return Err(LuaError::runtime(format_args!(
4510 "error while handling stack overflow (C stack overflow)"
4511 )));
4512 }
4513 Ok(())
4514}
4515
4516pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4527 state.n_ccalls += 1;
4528 if state.c_calls() >= LUAI_MAXCCALLS {
4530 check_c_stack(state)?;
4531 }
4532 Ok(())
4533}
4534
4535fn stack_init(thread: &mut LuaState) {
4541 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
4543 thread.stack = vec![StackValue::default(); total_slots];
4544
4545 thread.tbclist = Vec::new();
4549
4550 thread.top = StackIdx(0);
4555
4556 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
4557
4558
4559 let base_ci = CallInfo {
4560 func: StackIdx(0),
4561 top: StackIdx(1 + LUA_MINSTACK as u32),
4562 previous: None,
4563 next: None,
4564 callstatus: CIST_C,
4565 nresults: 0,
4566 u: CallInfoFrame::c_default(),
4567 u2: CallInfoExtra::default(),
4568 };
4569
4570 if thread.call_info.is_empty() {
4571 thread.call_info.push(base_ci);
4572 } else {
4573 thread.call_info[0] = base_ci;
4574 thread.call_info.truncate(1);
4575 }
4576
4577 thread.stack[0] = StackValue { val: LuaValue::Nil, tbc_delta: 0 };
4578
4579 thread.top = StackIdx(1);
4580
4581 thread.ci = CallInfoIdx(0);
4582}
4583
4584fn free_stack(state: &mut LuaState) {
4585 if state.stack.is_empty() {
4586 return;
4587 }
4588 state.ci = CallInfoIdx(0);
4589 free_ci(state);
4590 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
4591 state.stack.clear();
4593 state.stack.shrink_to_fit();
4594}
4595
4596fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
4597 let registry = state.new_table();
4599
4600 state.global_mut().l_registry = LuaValue::Table(registry.clone());
4602
4603 let globals = state.new_table();
4623 state.global_mut().globals = LuaValue::Table(globals);
4624 let loaded = state.new_table();
4625 state.global_mut().loaded = LuaValue::Table(loaded);
4626
4627 Ok(())
4628}
4629
4630fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
4631 stack_init(state);
4632 init_registry(state)?;
4633 crate::string::init(state)?;
4634 crate::tagmethods::init(state)?;
4635 state.global_mut().gcstp = 0;
4637 state.global().heap.unpause();
4638 state.global_mut().nilvalue = LuaValue::Nil;
4641 Ok(())
4643}
4644
4645fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
4646 thread.global = global;
4647 thread.stack = Vec::new();
4648 thread.call_info = Vec::new();
4649 thread.ci = CallInfoIdx(0);
4652 thread.nci = 0;
4653 thread.n_ccalls = 0;
4657 thread.hook = None;
4658 thread.hookmask = 0;
4659 thread.basehookcount = 0;
4660 thread.allowhook = true;
4661 thread.hookcount = thread.basehookcount;
4663
4664 {
4669 let (active, interval) = {
4670 let g = thread.global.borrow();
4671 (g.sandbox_active(), g.sandbox.interval.get())
4672 };
4673 if active {
4674 thread.hookmask = SANDBOX_COUNT_MASK;
4675 thread.basehookcount = interval;
4676 thread.hookcount = interval;
4677 }
4678 }
4679 thread.openupval = Vec::new();
4680 thread.status = LuaStatus::Ok as u8;
4681 thread.errfunc = 0;
4682 thread.oldpc = 0;
4683 thread.gc_check_needed = true;
4684}
4685
4686fn close_state(state: &mut LuaState) {
4687 let is_complete = state.global().is_complete();
4688
4689 if !is_complete {
4690 state.gc().free_all_objects();
4692 } else {
4693 state.ci = CallInfoIdx(0);
4694 state.gc().free_all_objects();
4697 }
4699
4700 state.global_mut().strt = StringPool::default();
4702
4703 free_stack(state);
4704
4705 }
4710
4711pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
4740 state.gc().check_step();
4741
4742 let global_rc = state.global_rc();
4747 let hookmask = state.hookmask;
4748 let basehookcount = state.basehookcount;
4749
4750 let reserved_id = {
4751 let mut g = state.global_mut();
4752 let id = g.next_thread_id;
4753 g.next_thread_id += 1;
4754 id
4755 };
4756
4757 let mut new_thread = LuaState {
4758 status: LuaStatus::Ok as u8,
4759 allowhook: true,
4760 nci: 0,
4761 top: StackIdx(0),
4762 stack_last: StackIdx(0),
4763 stack: Vec::new(),
4764 ci: CallInfoIdx(0),
4765 call_info: Vec::new(),
4766 openupval: Vec::new(),
4767 tbclist: Vec::new(),
4768 global: global_rc.clone(),
4769 hook: None,
4770 hookmask: 0,
4771 basehookcount: 0,
4772 hookcount: 0,
4773 errfunc: 0,
4774 n_ccalls: 0,
4775 oldpc: 0,
4776 marked: 0,
4777 cached_thread_id: reserved_id,
4778 gc_check_needed: false,
4779 };
4780
4781 preinit_thread(&mut new_thread, global_rc);
4782
4783 new_thread.hookmask = hookmask;
4784 new_thread.basehookcount = basehookcount;
4785 new_thread.reset_hook_count();
4788
4789 stack_init(&mut new_thread);
4795
4796 if let Some(body) = initial_body {
4797 new_thread.push(body);
4798 }
4799
4800 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
4801
4802 let value = {
4803 let mut g = state.global_mut();
4804 let id = reserved_id;
4805 let value = GcRef::new(lua_types::value::LuaThread::new(id));
4806 g.threads.insert(
4807 id,
4808 ThreadRegistryEntry { state: thread_ref, value: value.clone() },
4809 );
4810 value
4811 };
4812
4813 state.push(LuaValue::Thread(value));
4814
4815 Ok(())
4816}
4817
4818pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
4840 state.ci = CallInfoIdx(0);
4841 let ci_idx = 0usize;
4842
4843 if !state.stack.is_empty() {
4845 state.stack[0].val = LuaValue::Nil;
4846 }
4847
4848 state.call_info[ci_idx].func = StackIdx(0);
4849 state.call_info[ci_idx].callstatus = CIST_C;
4850
4851 let mut status = if status == LuaStatus::Yield as i32 {
4852 LuaStatus::Ok as i32
4853 } else {
4854 status
4855 };
4856
4857 state.status = LuaStatus::Ok as u8;
4858
4859 let close_status = crate::do_::close_protected(
4860 state,
4861 StackIdx(1),
4862 LuaStatus::from_raw(status),
4863 );
4864 status = close_status as i32;
4865
4866 if status != LuaStatus::Ok as i32 {
4867 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
4868 } else {
4869 state.top = StackIdx(1);
4870 }
4871
4872 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
4873 state.call_info[ci_idx].top = new_ci_top;
4874
4875 let needed = new_ci_top.0 as usize;
4878 if state.stack.len() < needed {
4879 state.stack.resize(needed, StackValue::default());
4880 }
4881
4882 status
4883}
4884
4885pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
4899 state.n_ccalls = match from {
4901 Some(f) => f.c_calls(),
4902 None => 0,
4903 };
4904 let current_status = state.status as i32;
4905 let result = reset_thread(state, current_status);
4906 result
4907}
4908
4909pub fn reset_thread_api(state: &mut LuaState) -> i32 {
4918 close_thread(state, None)
4919}
4920
4921pub fn new_state() -> Option<LuaState> {
4957 let placeholder_str = GcRef::new(LuaString::placeholder());
4966
4967 let initial_white = 1u8 << WHITE0BIT;
4969
4970 let global = GlobalState {
4974 parser_hook: None,
4975 cli_argv: None,
4976 cli_preload: None,
4977 lua_version: lua_types::LuaVersion::default(),
4978 file_loader_hook: None,
4979 file_open_hook: None,
4980 stdout_hook: None,
4981 stderr_hook: None,
4982 stdin_hook: None,
4983 env_hook: None,
4984 unix_time_hook: None,
4985 cpu_clock_hook: None,
4986 local_offset_hook: None,
4987 entropy_hook: None,
4988 temp_name_hook: None,
4989 popen_hook: None,
4990 file_remove_hook: None,
4991 file_rename_hook: None,
4992 os_execute_hook: None,
4993 dynlib_load_hook: None,
4994 dynlib_symbol_hook: None,
4995 dynlib_unload_hook: None,
4996 totalbytes: std::mem::size_of::<GlobalState>() as isize,
4997 sandbox: SandboxLimits::default(),
4998 gc_debt: 0,
4999 gc_estimate: 0,
5000 lastatomic: 0,
5001 strt: StringPool::default(),
5002 l_registry: LuaValue::Nil,
5003 external_roots: ExternalRootSet::default(),
5004 globals: LuaValue::Nil,
5005 loaded: LuaValue::Nil,
5006 nilvalue: LuaValue::Int(0),
5007 seed: make_seed(),
5008 currentwhite: initial_white,
5009 gcstate: GCS_PAUSE,
5010 gckind: GcKind::Incremental as u8,
5012 gcstopem: false,
5013 genminormul: LUAI_GENMINORMUL,
5014 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5016 gcstp: GCSTPGC,
5017 gcemergency: false,
5018 gcpause: (LUAI_GCPAUSE / 4) as u8,
5019 gcstepmul: (LUAI_GCMUL / 4) as u8,
5020 gcstepsize: LUAI_GCSTEPSIZE,
5021 gc55_params: [20, 50, 68, 250, 200, 9600],
5024 sweepgc_cursor: 0,
5025 weak_tables_registry: Vec::new(),
5026 pending_finalizers: Vec::new(),
5027 to_be_finalized: Vec::new(),
5028 gc_finalizer_error: None,
5029 twups: Vec::new(),
5030 panic: None,
5031 mainthread: None,
5032 threads: std::collections::HashMap::new(),
5033 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5034 current_thread_id: 0,
5035 main_thread_id: 0,
5036 next_thread_id: 1,
5037 memerrmsg: placeholder_str.clone(),
5038 tmname: Vec::new(),
5039 mt: std::array::from_fn(|_| None),
5040 strcache: std::array::from_fn(|_| {
5041 std::array::from_fn(|_| placeholder_str.clone())
5042 }),
5043 interned_lt: std::collections::HashMap::new(),
5044 warnf: None,
5045 warn_mode: WarnMode::Off,
5046 test_warn_enabled: false,
5047 test_warn_on: false,
5048 test_warn_mode: TestWarnMode::Normal,
5049 test_warn_last_to_cont: false,
5050 test_warn_buffer: Vec::new(),
5051 c_functions: Vec::new(),
5052 heap: lua_gc::Heap::new(),
5053 cross_thread_upvals: std::collections::HashMap::new(),
5054 suspended_parent_stacks: Vec::new(),
5055 suspended_parent_open_upvals: Vec::new(),
5056 };
5057
5058 let global_rc = Rc::new(RefCell::new(global));
5059
5060 let initial_marked = initial_white;
5062
5063 let mut main_thread = LuaState {
5064 status: LuaStatus::Ok as u8,
5065 allowhook: true,
5066 nci: 0,
5067 top: StackIdx(0),
5068 stack_last: StackIdx(0),
5069 stack: Vec::new(),
5070 ci: CallInfoIdx(0),
5071 call_info: Vec::new(),
5072 openupval: Vec::new(),
5073 tbclist: Vec::new(),
5074 global: global_rc.clone(),
5075 hook: None,
5076 hookmask: 0,
5077 basehookcount: 0,
5078 hookcount: 0,
5079 errfunc: 0,
5080 n_ccalls: 0,
5081 oldpc: 0,
5082 marked: initial_marked,
5083 cached_thread_id: 0,
5084 gc_check_needed: false,
5085 };
5086
5087 preinit_thread(&mut main_thread, global_rc.clone());
5088
5089 main_thread.inc_nny();
5091
5092 match lua_open(&mut main_thread) {
5102 Ok(()) => {}
5103 Err(_) => {
5104 close_state(&mut main_thread);
5105 return None;
5106 }
5107 }
5108
5109 Some(main_thread)
5110}
5111
5112pub fn close(mut state: LuaState) {
5128 close_state(&mut state);
5133}
5134
5135pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5145 let test_warn_enabled = state.global().test_warn_enabled;
5146 if test_warn_enabled {
5147 test_warn(state, msg, to_cont);
5148 return;
5149 }
5150
5151 let has_warnf = state.global().warnf.is_some();
5160 if has_warnf {
5161 let mut warnf = state.global_mut().warnf.take();
5163 if let Some(ref mut f) = warnf {
5164 f(msg, to_cont);
5165 }
5166 state.global_mut().warnf = warnf;
5168 return;
5169 }
5170 default_warn(state, msg, to_cont);
5171}
5172
5173fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5174 let is_control = {
5175 let g = state.global();
5176 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5177 };
5178 if is_control {
5179 let mut g = state.global_mut();
5180 match &msg[1..] {
5181 b"off" => g.test_warn_on = false,
5182 b"on" => g.test_warn_on = true,
5183 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5184 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5185 b"store" => g.test_warn_mode = TestWarnMode::Store,
5186 _ => {}
5187 }
5188 return;
5189 }
5190
5191 let finished = {
5192 let mut g = state.global_mut();
5193 g.test_warn_last_to_cont = to_cont;
5194 g.test_warn_buffer.extend_from_slice(msg);
5195 if to_cont {
5196 None
5197 } else {
5198 Some((
5199 std::mem::take(&mut g.test_warn_buffer),
5200 g.test_warn_mode,
5201 g.test_warn_on,
5202 ))
5203 }
5204 };
5205
5206 let Some((message, mode, warn_on)) = finished else {
5207 return;
5208 };
5209 match mode {
5210 TestWarnMode::Normal => {
5211 if warn_on && message.first() == Some(&b'#') {
5212 write_warning_message(&message);
5213 }
5214 }
5215 TestWarnMode::Allow => {
5216 if warn_on {
5217 write_warning_message(&message);
5218 }
5219 }
5220 TestWarnMode::Store => {
5221 if let Ok(s) = state.intern_str(&message) {
5222 state.push(LuaValue::Str(s));
5223 let _ = crate::api::set_global(state, b"_WARN");
5224 }
5225 }
5226 }
5227}
5228
5229fn write_warning_message(message: &[u8]) {
5230 use std::io::Write;
5231 let stderr = std::io::stderr();
5232 let mut h = stderr.lock();
5233 let _ = h.write_all(b"Lua warning: ");
5234 let _ = h.write_all(message);
5235 let _ = h.write_all(b"\n");
5236}
5237
5238fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5243 use std::io::Write;
5244 if !to_cont && msg.first() == Some(&b'@') {
5246 match &msg[1..] {
5247 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5248 b"on" => state.global_mut().warn_mode = WarnMode::On,
5249 _ => {}
5250 }
5251 return;
5252 }
5253 let mode = state.global().warn_mode;
5254 match mode {
5255 WarnMode::Off => {}
5256 WarnMode::On | WarnMode::Cont => {
5257 let stderr = std::io::stderr();
5258 let mut h = stderr.lock();
5259 if mode == WarnMode::On {
5260 let _ = h.write_all(b"Lua warning: ");
5261 }
5262 let _ = h.write_all(msg);
5263 if to_cont {
5264 state.global_mut().warn_mode = WarnMode::Cont;
5265 } else {
5266 let _ = h.write_all(b"\n");
5267 state.global_mut().warn_mode = WarnMode::On;
5268 }
5269 }
5270 }
5271}
5272
5273#[cfg(test)]
5274mod tests {
5275 use super::*;
5276
5277 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5278 Ok(0)
5279 }
5280
5281 #[test]
5282 fn external_root_keys_reject_stale_slot_after_reuse() {
5283 let mut roots = ExternalRootSet::default();
5284
5285 let first = roots.insert(LuaValue::Int(1));
5286 assert_eq!(roots.len(), 1);
5287 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5288
5289 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5290 assert!(roots.get(first).is_none());
5291 assert!(roots.remove(first).is_none());
5292 assert_eq!(roots.len(), 0);
5293 assert_eq!(roots.vacant_len(), 1);
5294 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5295 assert!(roots.is_empty());
5296
5297 let second = roots.insert(LuaValue::Int(2));
5298 assert_eq!(first.index, second.index);
5299 assert_ne!(first, second);
5300 assert!(roots.get(first).is_none());
5301 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5302 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5303 }
5304
5305 #[test]
5306 fn external_roots_keep_heap_value_alive_until_unrooted() {
5307 let mut state = new_state().expect("state should initialize");
5308 let _heap_guard = {
5309 let g = state.global();
5310 lua_gc::HeapGuard::push(&g.heap)
5311 };
5312
5313 let table = state.new_table();
5314 assert_eq!(state.global().heap.allgc_count(), 1);
5315
5316 let key = state.external_root_value(LuaValue::Table(table));
5317 state.gc().full_collect();
5318 assert_eq!(state.global().heap.allgc_count(), 1);
5319 assert_eq!(state.global().external_roots.len(), 1);
5320
5321 assert!(state.external_unroot_value(key).is_some());
5322 state.gc().full_collect();
5323 assert_eq!(state.global().heap.allgc_count(), 0);
5324 assert!(state.global().external_roots.is_empty());
5325 }
5326
5327 #[test]
5328 fn table_buffer_accounting_refunds_on_sweep() {
5329 let mut state = new_state().expect("state should initialize");
5330 let _heap_guard = {
5331 let g = state.global();
5332 lua_gc::HeapGuard::push(&g.heap)
5333 };
5334
5335 let table = state.new_table();
5336 let key = state.external_root_value(LuaValue::Table(table));
5337 let header_bytes = state.global().heap.bytes_used();
5338 assert!(header_bytes > 0);
5339
5340 for i in 1..=128 {
5341 table
5342 .raw_set_int(&mut state, i, LuaValue::Int(i))
5343 .expect("integer table insert should succeed");
5344 }
5345 let grown_bytes = state.global().heap.bytes_used();
5346 assert!(
5347 grown_bytes > header_bytes,
5348 "table array/hash buffer growth must be charged to the GC heap"
5349 );
5350
5351 state.gc().full_collect();
5352 assert_eq!(
5353 state.global().heap.bytes_used(),
5354 grown_bytes,
5355 "rooted table buffer bytes should remain charged after collection"
5356 );
5357
5358 assert!(state.external_unroot_value(key).is_some());
5359 state.gc().full_collect();
5360 assert_eq!(state.global().heap.bytes_used(), 0);
5361 assert_eq!(state.global().heap.allgc_count(), 0);
5362 }
5363
5364 #[test]
5365 fn string_buffer_accounting_refunds_on_sweep() {
5366 let mut state = new_state().expect("state should initialize");
5367 let _heap_guard = {
5368 let g = state.global();
5369 lua_gc::HeapGuard::push(&g.heap)
5370 };
5371
5372 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
5373 let string = state.intern_str(&payload).expect("long string should allocate");
5374 let key = state.external_root_value(LuaValue::Str(string));
5375 let allocated_bytes = state.global().heap.bytes_used();
5376 assert!(
5377 allocated_bytes > payload.len(),
5378 "long string backing bytes must be charged to the GC heap"
5379 );
5380
5381 state.gc().full_collect();
5382 assert_eq!(
5383 state.global().heap.bytes_used(),
5384 allocated_bytes,
5385 "rooted string buffer bytes should remain charged after collection"
5386 );
5387
5388 assert!(state.external_unroot_value(key).is_some());
5389 state.gc().full_collect();
5390 assert_eq!(state.global().heap.bytes_used(), 0);
5391 assert_eq!(state.global().heap.allgc_count(), 0);
5392 }
5393
5394 #[test]
5395 fn interned_short_string_cache_does_not_root_unreferenced_string() {
5396 let mut state = new_state().expect("state should initialize");
5397 let _heap_guard = {
5398 let g = state.global();
5399 lua_gc::HeapGuard::push(&g.heap)
5400 };
5401
5402 let payload = b"weak-cache-probe-a";
5403 let string = state
5404 .intern_str(payload)
5405 .expect("short string should intern");
5406 let id = string.identity();
5407 assert!(state.global().interned_lt.contains_key(&payload[..]));
5408 assert!(state.global().heap.allocation_token(id).is_some());
5409
5410 state.gc().full_collect();
5411 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5412 assert_eq!(state.global().heap.allocation_token(id), None);
5413 }
5414
5415 #[test]
5416 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
5417 let mut state = new_state().expect("state should initialize");
5418 let _heap_guard = {
5419 let g = state.global();
5420 lua_gc::HeapGuard::push(&g.heap)
5421 };
5422
5423 let payload = b"weak-cache-probe-b";
5424 let string = state
5425 .intern_str(payload)
5426 .expect("short string should intern");
5427 let id = string.identity();
5428 let key = state.external_root_value(LuaValue::Str(string));
5429
5430 state.gc().full_collect();
5431 assert!(state.global().interned_lt.contains_key(&payload[..]));
5432 assert!(state.global().heap.allocation_token(id).is_some());
5433
5434 assert!(state.external_unroot_value(key).is_some());
5435 state.gc().full_collect();
5436 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5437 assert_eq!(state.global().heap.allocation_token(id), None);
5438 }
5439
5440 #[test]
5441 fn gc_phase_predicates_follow_heap_state() {
5442 let mut state = new_state().expect("state should initialize");
5443 let _heap_guard = {
5444 let g = state.global();
5445 lua_gc::HeapGuard::push(&g.heap)
5446 };
5447
5448 {
5449 let mut g = state.global_mut();
5450 g.gckind = GcKind::Incremental as u8;
5451 g.lastatomic = 0;
5452 assert!(!g.is_gen_mode());
5453 g.lastatomic = 1;
5454 assert!(g.is_gen_mode());
5455 g.lastatomic = 0;
5456 }
5457
5458 let mut roots = Vec::new();
5459 for _ in 0..16 {
5460 let table = state.new_table();
5461 roots.push(state.external_root_value(LuaValue::Table(table)));
5462 }
5463
5464 let mut saw_keep = false;
5465 let mut saw_sweep = false;
5466 for _ in 0..128 {
5467 state.gc().incremental_step(1);
5468 let g = state.global();
5469 let heap_state = g.heap.gc_state();
5470 assert_eq!(
5471 g.keep_invariant(),
5472 matches!(heap_state, lua_gc::GcState::Propagate | lua_gc::GcState::Atomic)
5473 );
5474 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
5475 saw_keep |= g.keep_invariant();
5476 saw_sweep |= g.is_sweep_phase();
5477 if heap_state.is_pause() && saw_keep && saw_sweep {
5478 break;
5479 }
5480 }
5481
5482 assert!(saw_keep, "incremental cycle should expose an invariant phase");
5483 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
5484
5485 for key in roots {
5486 assert!(state.external_unroot_value(key).is_some());
5487 }
5488 state.gc().full_collect();
5489 }
5490
5491 #[test]
5492 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
5493 let mut state = new_state().expect("state should initialize");
5494 let _heap_guard = {
5495 let g = state.global();
5496 lua_gc::HeapGuard::push(&g.heap)
5497 };
5498
5499 let parent = state.new_table();
5500 let parent_key = state.external_root_value(LuaValue::Table(parent));
5501 state.gc().incremental_step(1);
5502 assert!(
5503 state.global().keep_invariant(),
5504 "test setup should leave the parent marked during an active cycle"
5505 );
5506
5507 let child = state.new_table();
5508 let parent_value = LuaValue::Table(parent);
5509 let child_value = LuaValue::Table(child);
5510 parent
5511 .raw_set_int(&mut state, 1, child_value)
5512 .expect("table store should succeed");
5513 state.gc_barrier_back(&parent_value, &child_value);
5514
5515 for _ in 0..128 {
5516 if state.gc().incremental_step(1) {
5517 break;
5518 }
5519 }
5520
5521 assert_eq!(state.global().heap.allgc_count(), 2);
5522 assert_eq!(
5523 parent.get_int(1).as_table().map(|t| t.identity()),
5524 Some(child.identity())
5525 );
5526
5527 assert!(state.external_unroot_value(parent_key).is_some());
5528 state.gc().full_collect();
5529 assert_eq!(state.global().heap.allgc_count(), 0);
5530 }
5531
5532 #[test]
5533 fn generational_mode_promotes_and_barriers_age_objects() {
5534 let mut state = new_state().expect("state should initialize");
5535 let _heap_guard = {
5536 let g = state.global();
5537 lua_gc::HeapGuard::push(&g.heap)
5538 };
5539
5540 let parent = state.new_table();
5541 let parent_key = state.external_root_value(LuaValue::Table(parent));
5542
5543 state.gc().change_mode(GcKind::Generational);
5544 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
5545 assert_eq!(parent.0.color(), lua_gc::Color::Black);
5546 let majorbase = state.global().gc_estimate;
5547 assert!(majorbase > 0);
5548 assert!(state.global().gc_debt() <= 0);
5549
5550 let child = state.new_table();
5551 let parent_value = LuaValue::Table(parent);
5552 let child_value = LuaValue::Table(child);
5553 parent
5554 .raw_set_int(&mut state, 1, child_value.clone())
5555 .expect("table store should succeed");
5556 state.gc_barrier_back(&parent_value, &child_value);
5557 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
5558 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
5559 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5560
5561 let metatable = state.new_table();
5562 parent.set_metatable(Some(metatable));
5563 state.gc().obj_barrier(&parent, &metatable);
5564 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
5565
5566 assert!(state.gc().generational_step());
5567 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
5568 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
5569 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
5570 assert_eq!(state.global().gc_estimate, majorbase);
5571 assert!(state.global().gc_debt() <= 0);
5572
5573 state.gc().change_mode(GcKind::Incremental);
5574 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
5575 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5576 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
5577
5578 assert!(state.external_unroot_value(parent_key).is_some());
5579 state.gc().full_collect();
5580 }
5581
5582 #[test]
5583 fn generational_upvalue_write_barrier_marks_young_child_old0() {
5584 let mut state = new_state().expect("state should initialize");
5585 let _heap_guard = {
5586 let g = state.global();
5587 lua_gc::HeapGuard::push(&g.heap)
5588 };
5589
5590 let proto = state.new_proto();
5591 let closure = state.new_lclosure(proto, 1);
5592 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5593 state.gc().change_mode(GcKind::Generational);
5594 let uv = closure.upval(0);
5595 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
5596
5597 let child = state.new_table();
5598 state
5599 .upvalue_set(&closure, 0, LuaValue::Table(child))
5600 .expect("closed upvalue write should succeed");
5601 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5602
5603 assert!(state.external_unroot_value(closure_key).is_some());
5604 state.gc().full_collect();
5605 }
5606
5607 #[test]
5608 fn cclosure_setupvalue_replaces_upvalue() {
5609 let mut state = new_state().expect("state should initialize");
5610 let _heap_guard = {
5611 let g = state.global();
5612 lua_gc::HeapGuard::push(&g.heap)
5613 };
5614
5615 let first = state.new_table();
5616 state.push(LuaValue::Table(first));
5617 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
5618 .expect("C closure creation should succeed");
5619 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5620 panic!("expected heavy C closure");
5621 };
5622
5623 let second = state.new_table();
5624 state.push(LuaValue::Table(second));
5625 let name = crate::api::setup_value(&mut state, -2, 1)
5626 .expect("C closure upvalue should exist");
5627
5628 assert!(name.is_empty());
5629 let upvalues = ccl.upvalues.borrow();
5630 let LuaValue::Table(actual) = upvalues[0].clone() else {
5631 panic!("expected table upvalue");
5632 };
5633 assert_eq!(actual.identity(), second.identity());
5634 }
5635
5636 #[test]
5637 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
5638 let mut state = new_state().expect("state should initialize");
5639 let _heap_guard = {
5640 let g = state.global();
5641 lua_gc::HeapGuard::push(&g.heap)
5642 };
5643
5644 state.push(LuaValue::Nil);
5645 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
5646 .expect("C closure creation should succeed");
5647 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5648 panic!("expected heavy C closure");
5649 };
5650 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
5651
5652 state.gc().change_mode(GcKind::Generational);
5653 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
5654
5655 let child = state.new_table();
5656 state.push(LuaValue::Table(child));
5657 crate::api::setup_value(&mut state, -2, 1)
5658 .expect("C closure upvalue should exist");
5659
5660 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5661
5662 assert!(state.external_unroot_value(closure_key).is_some());
5663 state.gc().full_collect();
5664 }
5665
5666 #[test]
5667 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
5668 let mut state = new_state().expect("state should initialize");
5669 let _heap_guard = {
5670 let g = state.global();
5671 lua_gc::HeapGuard::push(&g.heap)
5672 };
5673
5674 let proto = state.new_proto();
5675 let closure = state.new_lclosure(proto, 1);
5676 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5677 state.gc().change_mode(GcKind::Generational);
5678 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
5679
5680 let replacement = state.new_upval_closed(LuaValue::Nil);
5681 closure.set_upval(0, replacement);
5682 state.gc().obj_barrier(&closure, &replacement);
5683 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
5684
5685 assert!(state.external_unroot_value(closure_key).is_some());
5686 state.gc().full_collect();
5687 }
5688
5689 #[test]
5690 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
5691 let mut state = new_state().expect("state should initialize");
5692 let _heap_guard = {
5693 let g = state.global();
5694 lua_gc::HeapGuard::push(&g.heap)
5695 };
5696
5697 let mirrored = state.new_table();
5698 state
5699 .global_mut()
5700 .cross_thread_upvals
5701 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
5702
5703 state.gc().full_collect();
5704 assert_eq!(state.global().heap.allgc_count(), 1);
5705
5706 state.global_mut().cross_thread_upvals.clear();
5707 state.gc().full_collect();
5708 assert_eq!(state.global().heap.allgc_count(), 0);
5709 }
5710
5711 #[test]
5712 fn generational_full_collect_promotes_new_survivors_to_old() {
5713 let mut state = new_state().expect("state should initialize");
5714 let _heap_guard = {
5715 let g = state.global();
5716 lua_gc::HeapGuard::push(&g.heap)
5717 };
5718
5719 state.gc().change_mode(GcKind::Generational);
5720 let table = state.new_table();
5721 let table_key = state.external_root_value(LuaValue::Table(table));
5722 assert_eq!(table.0.age(), lua_gc::GcAge::New);
5723
5724 state.gc().full_collect();
5725 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
5726 assert_eq!(table.0.color(), lua_gc::Color::Black);
5727
5728 assert!(state.external_unroot_value(table_key).is_some());
5729 state.gc().full_collect();
5730 }
5731
5732 #[test]
5733 fn gc_packed_params_return_user_visible_values() {
5734 let mut state = new_state().expect("state should initialize");
5735 assert_eq!(
5736 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
5737 200
5738 );
5739 assert_eq!(state.global().gc_pause_param(), 200);
5740 assert_eq!(
5741 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
5742 100
5743 );
5744 assert_eq!(state.global().gc_stepmul_param(), 200);
5745
5746 crate::api::gc(
5747 &mut state,
5748 crate::api::GcArgs::Gen {
5749 minormul: 0,
5750 majormul: 200,
5751 },
5752 );
5753 assert_eq!(state.global().gc_genmajormul_param(), 200);
5754 }
5755
5756 #[test]
5757 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
5758 let mut state = new_state().expect("state should initialize");
5759 let _heap_guard = {
5760 let g = state.global();
5761 lua_gc::HeapGuard::push(&g.heap)
5762 };
5763
5764 let root = state.new_table();
5765 let root_key = state.external_root_value(LuaValue::Table(root));
5766 state.gc().change_mode(GcKind::Generational);
5767
5768 let root_value = LuaValue::Table(root);
5769 for i in 1..=64 {
5770 let child = state.new_table();
5771 let child_value = LuaValue::Table(child);
5772 root
5773 .raw_set_int(&mut state, i, child_value.clone())
5774 .expect("table store should succeed");
5775 state.gc_barrier_back(&root_value, &child_value);
5776 }
5777
5778 {
5779 let mut g = state.global_mut();
5780 g.gc_estimate = 1;
5781 set_debt(&mut *g, 1);
5782 }
5783
5784 assert!(state.gc().generational_step());
5785 let g = state.global();
5786 assert!(g.is_gen_mode());
5787 assert!(g.lastatomic > 0, "bad major collection should arm stepgenfull");
5788 assert!(g.gc_estimate > 1);
5789 assert!(g.gc_debt() <= 0);
5790 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
5791 drop(g);
5792
5793 assert!(state.external_unroot_value(root_key).is_some());
5794 state.gc().full_collect();
5795 }
5796
5797 #[test]
5798 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
5799 let mut state = new_state().expect("state should initialize");
5800 let _heap_guard = {
5801 let g = state.global();
5802 lua_gc::HeapGuard::push(&g.heap)
5803 };
5804
5805 let root = state.new_table();
5806 let root_key = state.external_root_value(LuaValue::Table(root));
5807 state.gc().change_mode(GcKind::Generational);
5808 {
5809 let mut g = state.global_mut();
5810 g.lastatomic = 1024;
5811 }
5812
5813 assert!(state.gc().generational_step());
5814 let g = state.global();
5815 assert_eq!(g.gckind, GcKind::Generational as u8);
5816 assert_eq!(g.lastatomic, 0);
5817 assert!(g.gc_debt() <= 0);
5818 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
5819 assert_eq!(root.0.color(), lua_gc::Color::Black);
5820 drop(g);
5821
5822 assert!(state.external_unroot_value(root_key).is_some());
5823 state.gc().full_collect();
5824 }
5825
5826 #[test]
5827 fn generational_step_zero_reports_false_without_positive_debt() {
5828 let mut state = new_state().expect("state should initialize");
5829 let _heap_guard = {
5830 let g = state.global();
5831 lua_gc::HeapGuard::push(&g.heap)
5832 };
5833
5834 state.gc().change_mode(GcKind::Generational);
5835 assert_eq!(
5836 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
5837 0
5838 );
5839 assert_eq!(
5840 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
5841 1
5842 );
5843 }
5844}
5845
5846