1use std::cell::RefCell;
22use std::collections::hash_map::Entry;
23use std::collections::HashMap;
24use std::hash::{BuildHasherDefault, Hasher};
25use std::rc::Rc;
26
27use crate::string::StringPool;
28pub use lua_types::error::LuaError;
29pub use lua_types::{CallInfoIdx, StackIdx};
30
31pub struct StackIdxConv(pub StackIdx);
34
35#[inline(always)]
40pub fn stack_idx_to_i32(i: StackIdx) -> i32 {
41 i.0 as i32
42}
43
44impl From<u32> for StackIdxConv {
45 #[inline(always)]
46 fn from(v: u32) -> Self {
47 StackIdxConv(StackIdx(v))
48 }
49}
50impl From<i32> for StackIdxConv {
51 #[inline(always)]
52 fn from(v: i32) -> Self {
53 StackIdxConv(StackIdx(v.max(0) as u32))
54 }
55}
56impl From<usize> for StackIdxConv {
57 #[inline(always)]
58 fn from(v: usize) -> Self {
59 StackIdxConv(StackIdx(v as u32))
60 }
61}
62impl From<StackIdx> for StackIdxConv {
63 #[inline(always)]
64 fn from(v: StackIdx) -> Self {
65 StackIdxConv(v)
66 }
67}
68pub use lua_types::closure::{
69 LuaCClosure as LuaClosureC, LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua,
70};
71pub use lua_types::gc::GcRef;
72pub use lua_types::proto::LuaProto;
73pub use lua_types::string::LuaString;
74pub use lua_types::upval::{UpVal, UpValState};
75pub use lua_types::userdata::LuaUserData;
76pub use lua_types::value::{F2Imod, LuaTable, LuaValue};
77
78pub struct LuaByteHasher {
79 hash: u64,
80}
81
82impl Default for LuaByteHasher {
83 fn default() -> Self {
84 Self {
85 hash: 0xcbf2_9ce4_8422_2325,
86 }
87 }
88}
89
90impl Hasher for LuaByteHasher {
91 #[inline]
92 fn write(&mut self, bytes: &[u8]) {
93 const PRIME: u64 = 0x0000_0100_0000_01b3;
94 for &byte in bytes {
95 self.hash ^= u64::from(byte);
96 self.hash = self.hash.wrapping_mul(PRIME);
97 }
98 }
99
100 #[inline]
101 fn write_u8(&mut self, i: u8) {
102 self.write(&[i]);
103 }
104
105 #[inline]
106 fn write_usize(&mut self, i: usize) {
107 self.write(&i.to_ne_bytes());
108 }
109
110 #[inline]
111 fn finish(&self) -> u64 {
112 self.hash
113 }
114}
115
116pub type LuaByteBuildHasher = BuildHasherDefault<LuaByteHasher>;
117pub type InternedStringMap = HashMap<Box<[u8]>, GcRef<LuaString>, LuaByteBuildHasher>;
118
119pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
126
127pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
128
129#[derive(Clone)]
130pub enum LuaCallable {
131 Bare(LuaCFunction),
132 Rust(LuaRustFunction),
133}
134
135impl std::fmt::Debug for LuaCallable {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
139 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
140 }
141 }
142}
143
144impl LuaCallable {
145 pub fn bare(f: LuaCFunction) -> Self {
146 LuaCallable::Bare(f)
147 }
148
149 pub fn rust(f: LuaRustFunction) -> Self {
150 LuaCallable::Rust(f)
151 }
152
153 pub fn as_bare(&self) -> Option<LuaCFunction> {
154 match self {
155 LuaCallable::Bare(f) => Some(*f),
156 LuaCallable::Rust(_) => None,
157 }
158 }
159
160 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
161 match self {
162 LuaCallable::Bare(f) => f(state),
163 LuaCallable::Rust(f) => f(state),
164 }
165 }
166}
167
168#[derive(Clone, Debug)]
169pub enum FinalizerObject {
170 Table(GcRef<LuaTable>),
171 UserData(GcRef<LuaUserData>),
172}
173
174impl FinalizerObject {
175 pub fn identity(&self) -> usize {
176 match self {
177 FinalizerObject::Table(t) => t.identity(),
178 FinalizerObject::UserData(u) => u.identity(),
179 }
180 }
181
182 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
183 match self {
184 FinalizerObject::Table(t) => t.metatable(),
185 FinalizerObject::UserData(u) => u.metatable(),
186 }
187 }
188
189 pub fn as_lua_value(&self) -> LuaValue {
190 match self {
191 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
192 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
193 }
194 }
195
196 pub fn mark(&self, marker: &mut lua_gc::Marker) {
197 match self {
198 FinalizerObject::Table(t) => marker.mark(t.0),
199 FinalizerObject::UserData(u) => marker.mark(u.0),
200 }
201 }
202
203 pub fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
204 Some(match self {
205 FinalizerObject::Table(t) => t.0.as_trace_ptr(),
206 FinalizerObject::UserData(u) => u.0.as_trace_ptr(),
207 })
208 }
209
210 pub fn age(&self) -> lua_gc::GcAge {
211 match self {
212 FinalizerObject::Table(t) => t.0.age(),
213 FinalizerObject::UserData(u) => u.0.age(),
214 }
215 }
216
217 pub fn is_finalized(&self) -> bool {
218 match self {
219 FinalizerObject::Table(t) => t.0.is_finalized(),
220 FinalizerObject::UserData(u) => u.0.is_finalized(),
221 }
222 }
223
224 pub fn set_finalized(&self, finalized: bool) {
225 match self {
226 FinalizerObject::Table(t) => t.0.set_finalized(finalized),
227 FinalizerObject::UserData(u) => u.0.set_finalized(finalized),
228 }
229 }
230}
231
232impl lua_gc::FinalizerEntry for FinalizerObject {
233 fn identity(&self) -> usize {
234 FinalizerObject::identity(self)
235 }
236
237 fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
238 FinalizerObject::heap_ptr(self)
239 }
240
241 fn age(&self) -> lua_gc::GcAge {
242 FinalizerObject::age(self)
243 }
244
245 fn is_finalized(&self) -> bool {
246 FinalizerObject::is_finalized(self)
247 }
248
249 fn set_finalized(&self, finalized: bool) {
250 FinalizerObject::set_finalized(self, finalized);
251 }
252}
253
254#[derive(Clone, Debug)]
255pub struct WeakTableEntry {
256 table: lua_types::gc::GcWeak<LuaTable>,
257 kind: lua_gc::WeakListKind,
258}
259
260impl WeakTableEntry {
261 pub fn new(table: &GcRef<LuaTable>) -> Self {
262 let mode = table.weak_mode();
263 let weak_keys = (mode & (1 << 0)) != 0;
264 let weak_values = (mode & (1 << 1)) != 0;
265 let kind = match (weak_keys, weak_values) {
266 (true, true) => lua_gc::WeakListKind::AllWeak,
267 (true, false) => lua_gc::WeakListKind::Ephemeron,
268 (false, true) => lua_gc::WeakListKind::WeakValues,
269 (false, false) => lua_gc::WeakListKind::WeakValues,
270 };
271 Self {
272 table: table.downgrade(),
273 kind,
274 }
275 }
276}
277
278impl lua_gc::WeakEntry for WeakTableEntry {
279 type Strong = GcRef<LuaTable>;
280
281 fn identity(&self) -> usize {
282 self.table.identity()
283 }
284
285 fn list_kind(&self) -> lua_gc::WeakListKind {
286 self.kind
287 }
288
289 fn upgrade(&self) -> Option<Self::Strong> {
290 self.table.upgrade()
291 }
292}
293
294pub(crate) const EXTRA_STACK: usize = 5;
298
299pub(crate) const LUA_MINSTACK: usize = 20;
301
302pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
304
305pub(crate) const LUAI_MAXCCALLS: u32 = 200;
325
326pub(crate) const CIST_C: u16 = 1 << 1;
328
329pub(crate) const CIST_OAH: u16 = 1 << 0;
331pub(crate) const CIST_FRESH: u16 = 1 << 2;
332pub(crate) const CIST_HOOKED: u16 = 1 << 3;
333pub(crate) const CIST_YPCALL: u16 = 1 << 4;
334pub(crate) const CIST_TAIL: u16 = 1 << 5;
335pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
336pub(crate) const CIST_FIN: u16 = 1 << 7;
337pub(crate) const CIST_TRAN: u16 = 1 << 8;
338pub(crate) const CIST_RECST: u32 = 10;
339pub(crate) const CIST_LEQ: u16 = 1 << 13;
346
347const LUA_NUMTYPES: usize = 9;
349
350const GCSTPUSR: u8 = 1;
352const GCSTPGC: u8 = 2;
353
354const GCS_PAUSE: u8 = 0;
356
357const LUAI_GCPAUSE: u32 = 200;
358const LUAI_GCMUL: u32 = 100;
359const LUAI_GCSTEPSIZE: u8 = 13;
360const LUAI_GENMAJORMUL: u32 = 100;
361const LUAI_GENMINORMUL: u8 = 20;
362
363const WHITE0BIT: u8 = 0;
364
365const STRCACHE_N: usize = 53;
366const STRCACHE_M: usize = 2;
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
374pub enum GcKind {
375 Incremental = 0,
376 Generational = 1,
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub enum WarnMode {
388 Off,
389 On,
390 Cont,
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395pub enum TestWarnMode {
396 Normal,
397 Allow,
398 Store,
399}
400
401pub use lua_types::status::LuaStatus;
406
407#[derive(Clone)]
414pub struct StackValue {
415 pub val: LuaValue,
416 pub tbc_delta: u16,
417}
418
419impl Default for StackValue {
420 fn default() -> Self {
421 StackValue {
422 val: LuaValue::Nil,
423 tbc_delta: 0,
424 }
425 }
426}
427
428#[derive(Clone)]
437pub struct CallInfo {
438 pub func: StackIdx,
440
441 pub top: StackIdx,
443
444 pub previous: Option<CallInfoIdx>,
446
447 pub next: Option<CallInfoIdx>,
449
450 pub u: CallInfoFrame,
451
452 pub u2: CallInfoExtra,
453
454 pub nresults: i16,
456
457 pub callstatus: u16,
459
460 pub call_metamethods: u8,
464}
465
466#[derive(Clone, Copy)]
469pub enum CallInfoFrame {
470 Lua {
471 savedpc: u32,
473 trap: bool,
475 nextraargs: i32,
477 },
478 C {
479 k: Option<LuaKFunction>,
481 old_errfunc: isize,
483 ctx: isize,
485 },
486}
487
488pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
490
491#[derive(Default, Clone, Copy)]
495pub struct CallInfoExtra {
496 pub value: i32,
497 pub ftransfer: u16,
498 pub ntransfer: u16,
499}
500
501impl CallInfoFrame {
502 pub fn c_default() -> Self {
504 CallInfoFrame::C {
505 k: None,
506 old_errfunc: 0,
507 ctx: 0,
508 }
509 }
510
511 pub fn lua_default() -> Self {
513 CallInfoFrame::Lua {
514 savedpc: 0,
515 trap: false,
516 nextraargs: 0,
517 }
518 }
519}
520
521impl Default for CallInfo {
522 fn default() -> Self {
523 CallInfo {
524 func: StackIdx(0),
525 top: StackIdx(0),
526 previous: None,
527 next: None,
528 u: CallInfoFrame::c_default(),
529 u2: CallInfoExtra::default(),
530 nresults: 0,
531 callstatus: 0,
532 call_metamethods: 0,
533 }
534 }
535}
536
537impl CallInfo {
538 pub fn is_lua(&self) -> bool {
539 (self.callstatus & CIST_C) == 0
540 }
541 pub fn is_lua_code(&self) -> bool {
542 self.is_lua()
543 }
544 pub fn is_vararg_func(&self) -> bool {
551 false
552 }
553 pub fn saved_pc(&self) -> u32 {
554 if let CallInfoFrame::Lua { savedpc, .. } = self.u {
555 savedpc
556 } else {
557 0
558 }
559 }
560 pub fn set_saved_pc(&mut self, pc: u32) {
561 if let CallInfoFrame::Lua {
562 ref mut savedpc, ..
563 } = self.u
564 {
565 *savedpc = pc;
566 }
567 }
568 pub fn nextra_args(&self) -> i32 {
569 if let CallInfoFrame::Lua { nextraargs, .. } = self.u {
570 nextraargs
571 } else {
572 0
573 }
574 }
575 pub fn transfer_ftransfer(&self) -> u16 {
576 self.u2.ftransfer
577 }
578 pub fn transfer_ntransfer(&self) -> u16 {
579 self.u2.ntransfer
580 }
581 pub fn set_trap(&mut self, t: bool) {
582 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u {
583 *trap = t;
584 }
585 }
586 pub fn recover_status(&self) -> i32 {
589 ((self.callstatus >> CIST_RECST) & 7) as i32
590 }
591 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
594 let st = (status.into() & 7) as u16;
595 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
596 }
597 pub fn get_oah(&self) -> bool {
598 (self.callstatus & CIST_OAH) != 0
599 }
600 pub fn set_oah(&mut self, allow: bool) {
603 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
604 }
605 pub fn u_c_old_errfunc(&self) -> isize {
606 if let CallInfoFrame::C { old_errfunc, .. } = self.u {
607 old_errfunc
608 } else {
609 0
610 }
611 }
612 pub fn u_c_ctx(&self) -> isize {
613 if let CallInfoFrame::C { ctx, .. } = self.u {
614 ctx
615 } else {
616 0
617 }
618 }
619 pub fn u_c_k(&self) -> Option<LuaKFunction> {
620 if let CallInfoFrame::C { k, .. } = self.u {
621 k
622 } else {
623 None
624 }
625 }
626 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
630 if let CallInfoFrame::C {
631 k: ref mut slot, ..
632 } = self.u
633 {
634 *slot = k;
635 }
636 }
637 pub fn set_u_c_ctx(&mut self, ctx: isize) {
639 if let CallInfoFrame::C {
640 ctx: ref mut slot, ..
641 } = self.u
642 {
643 *slot = ctx;
644 }
645 }
646 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
648 if let CallInfoFrame::C {
649 old_errfunc: ref mut slot,
650 ..
651 } = self.u
652 {
653 *slot = old_errfunc;
654 }
655 }
656 pub fn set_u2_funcidx(&mut self, idx: i32) {
659 self.u2.value = idx;
660 }
661}
662
663pub trait LuaValueExt {
669 fn base_type(&self) -> lua_types::LuaType;
670 fn to_number_no_strconv(&self) -> Option<f64>;
671 fn to_number_with_strconv(&self) -> Option<f64>;
672 fn to_integer_no_strconv(&self) -> Option<i64>;
673 fn to_integer_with_strconv(&self) -> Option<i64>;
674 fn full_type_tag(&self) -> u8;
675}
676
677impl LuaValueExt for LuaValue {
678 fn base_type(&self) -> lua_types::LuaType {
679 self.type_tag()
680 }
681 fn to_number_no_strconv(&self) -> Option<f64> {
682 match self {
683 LuaValue::Float(f) => Some(*f),
684 LuaValue::Int(i) => Some(*i as f64),
685 _ => None,
686 }
687 }
688 fn to_number_with_strconv(&self) -> Option<f64> {
689 if let Some(n) = self.to_number_no_strconv() {
690 return Some(n);
691 }
692 if let LuaValue::Str(s) = self {
693 let mut tmp = LuaValue::Nil;
694 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
695 if sz == 0 {
696 return None;
697 }
698 return match tmp {
699 LuaValue::Int(i) => Some(i as f64),
700 LuaValue::Float(f) => Some(f),
701 _ => None,
702 };
703 }
704 None
705 }
706 fn to_integer_no_strconv(&self) -> Option<i64> {
707 match self {
708 LuaValue::Int(i) => Some(*i),
709 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
710 let min_f = i64::MIN as f64;
714 let max_plus1_f = -(i64::MIN as f64);
715 if *f >= min_f && *f < max_plus1_f {
716 Some(*f as i64)
717 } else {
718 None
719 }
720 }
721 _ => None,
722 }
723 }
724 fn to_integer_with_strconv(&self) -> Option<i64> {
725 if let Some(i) = self.to_integer_no_strconv() {
726 return Some(i);
727 }
728 if let LuaValue::Str(s) = self {
729 let mut tmp = LuaValue::Nil;
730 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
731 if sz == 0 {
732 return None;
733 }
734 return tmp.to_integer_no_strconv();
735 }
736 None
737 }
738 fn full_type_tag(&self) -> u8 {
739 match self {
740 LuaValue::Nil => 0x00,
741 LuaValue::Bool(false) => 0x01,
742 LuaValue::Bool(true) => 0x11,
743 LuaValue::Int(_) => 0x03,
744 LuaValue::Float(_) => 0x13,
745 LuaValue::Str(s) if s.is_short() => 0x04,
746 LuaValue::Str(_) => 0x14,
747 LuaValue::LightUserData(_) => 0x02,
748 LuaValue::Table(_) => 0x05,
749 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
750 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
751 LuaValue::Function(LuaClosure::C(_)) => 0x26,
752 LuaValue::UserData(_) => 0x07,
753 LuaValue::Thread(_) => 0x08,
754 }
755 }
756}
757
758pub trait LuaTypeExt {
760 fn type_name(&self) -> &'static [u8];
761}
762
763impl LuaTypeExt for lua_types::LuaType {
764 fn type_name(&self) -> &'static [u8] {
765 use lua_types::LuaType::*;
766 match self {
767 None => b"no value",
768 Nil => b"nil",
769 Boolean => b"boolean",
770 LightUserData => b"userdata",
771 Number => b"number",
772 String => b"string",
773 Table => b"table",
774 Function => b"function",
775 UserData => b"userdata",
776 Thread => b"thread",
777 }
778 }
779}
780
781pub trait StackIdxExt {
785 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
786 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
787 fn raw(self) -> u32;
788}
789impl StackIdxExt for StackIdx {
790 #[inline(always)]
791 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 {
792 self.0.saturating_sub(n.into().0 .0)
793 }
794 #[inline(always)]
795 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 {
796 self.0.wrapping_sub(n.into().0 .0)
797 }
798 #[inline(always)]
799 fn raw(self) -> u32 {
800 self.0
801 }
802}
803
804pub trait LuaTableRefExt {
814 fn metatable(&self) -> Option<GcRef<LuaTable>>;
815 fn as_ptr(&self) -> *const ();
816 fn get(&self, _k: &LuaValue) -> LuaValue;
817 fn get_int(&self, _k: i64) -> LuaValue;
818 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
819 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
820 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
821 fn invalidate_tm_cache(&self);
822 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
823 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
824}
825impl LuaTableRefExt for GcRef<LuaTable> {
826 #[inline]
827 fn metatable(&self) -> Option<GcRef<LuaTable>> {
828 (**self).metatable()
829 }
830 #[inline]
831 fn as_ptr(&self) -> *const () {
832 GcRef::identity(self) as *const ()
833 }
834 #[inline]
835 fn get(&self, k: &LuaValue) -> LuaValue {
836 (**self).get(k)
837 }
838 #[inline]
839 fn get_int(&self, k: i64) -> LuaValue {
840 (**self).get_int(k)
841 }
842 #[inline]
843 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue {
844 (**self).get_short_str(k)
845 }
846 #[inline]
849 fn raw_set(&self, _state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
850 let before = (**self).buffer_bytes();
851 let result = (**self).try_raw_set(k, v);
852 if result.is_ok() {
853 account_table_buffer_delta(self, before);
854 }
855 result
856 }
857 #[inline]
858 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
859 let before = (**self).buffer_bytes();
860 let result = (**self).try_raw_set_int(k, v);
861 if result.is_ok() {
862 account_table_buffer_delta(self, before);
863 }
864 result
865 }
866 fn invalidate_tm_cache(&self) {}
867 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
868 let before = (**self).buffer_bytes();
869 let na32 = na.min(u32::MAX as usize) as u32;
870 let nh32 = nh.min(u32::MAX as usize) as u32;
871 let result = (**self).resize(na32, nh32);
872 if result.is_ok() {
873 account_table_buffer_delta(self, before);
874 }
875 result
876 }
877 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
878 (**self).try_next_pair(&k)
879 }
880}
881
882#[inline]
883fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
884 let after = (**t).buffer_bytes();
885 if after > before {
886 t.account_buffer((after - before) as isize);
887 } else if before > after {
888 t.account_buffer(-((before - after) as isize));
889 }
890}
891
892pub trait LuaUserDataRefExt {
893 fn metatable(&self) -> Option<GcRef<LuaTable>>;
894 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
895 fn as_ptr(&self) -> *const ();
896 fn len(&self) -> usize;
897}
898impl LuaUserDataRefExt for GcRef<LuaUserData> {
899 fn metatable(&self) -> Option<GcRef<LuaTable>> {
900 (**self).metatable()
901 }
902 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) {
903 (**self).set_metatable(mt);
904 }
905 fn as_ptr(&self) -> *const () {
906 GcRef::identity(self) as *const ()
907 }
908 fn len(&self) -> usize {
909 self.0.data.len()
910 }
911}
912
913pub trait LuaStringRefExt {
914 fn is_white(&self) -> bool;
915 fn hash(&self) -> u32;
916 fn as_gc_ref(&self) -> GcRef<LuaString>;
917}
918impl LuaStringRefExt for GcRef<LuaString> {
919 fn is_white(&self) -> bool {
920 false
921 }
922 fn hash(&self) -> u32 {
923 self.0.hash()
924 }
925 fn as_gc_ref(&self) -> GcRef<LuaString> {
926 self.clone()
927 }
928}
929
930pub trait LuaLClosureRefExt {
931 fn proto(&self) -> &GcRef<LuaProto>;
932 fn nupvalues(&self) -> usize;
933}
934impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
935 fn proto(&self) -> &GcRef<LuaProto> {
936 &self.0.proto
937 }
938 fn nupvalues(&self) -> usize {
939 self.0.upvals.len()
940 }
941}
942
943pub trait LuaClosureExt {
945 fn nupvalues(&self) -> usize;
946}
947impl LuaClosureExt for LuaClosure {
948 fn nupvalues(&self) -> usize {
949 match self {
950 LuaClosure::Lua(l) => l.0.upvals.len(),
951 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
952 LuaClosure::LightC(_) => 0,
953 }
954 }
955}
956
957pub trait LuaProtoExt {
959 fn source_bytes(&self) -> &[u8];
960 fn source_string(&self) -> Option<&GcRef<LuaString>>;
961}
962impl LuaProtoExt for LuaProto {
963 fn source_bytes(&self) -> &[u8] {
964 match &self.source {
965 Some(s) => s.0.as_bytes(),
966 None => &[],
967 }
968 }
969 fn source_string(&self) -> Option<&GcRef<LuaString>> {
970 self.source.as_ref()
971 }
972}
973
974pub trait Collectable: std::fmt::Debug {}
981
982impl std::fmt::Debug for LuaState {
983 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
984 write!(f, "LuaState")
985 }
986}
987impl Collectable for LuaState {}
988
989pub type ParserHook = fn(
998 state: &mut LuaState,
999 source: &[u8],
1000 name: &[u8],
1001 firstchar: i32,
1002) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
1003
1004pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
1012
1013pub type FileOpenHook =
1024 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
1025
1026pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
1034
1035pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
1038
1039pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
1045
1046pub type UnixTimeHook = fn() -> i64;
1048
1049pub type CpuClockHook = fn() -> f64;
1057
1058pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
1070
1071pub type EntropyHook = fn() -> u64;
1075
1076pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
1081
1082pub type PopenHook =
1093 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
1094
1095pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
1101
1102pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
1108
1109#[derive(Clone, Copy, Debug)]
1115pub enum OsExecuteReason {
1116 Exit,
1118 Signal,
1120}
1121
1122#[derive(Debug)]
1125pub struct OsExecuteResult {
1126 pub success: bool,
1128 pub reason: OsExecuteReason,
1130 pub code: i32,
1132}
1133
1134pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
1141
1142#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1149pub struct DynLibId(pub u64);
1150
1151pub enum DynamicSymbol {
1159 RustNative(LuaCFunction),
1162 LuaCAbi(*const ()),
1168 Unsupported { reason: Vec<u8> },
1171}
1172
1173pub type DynLibLoadHook =
1184 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
1185
1186pub type DynLibSymbolHook =
1194 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
1195
1196pub type DynLibUnloadHook = fn(handle: DynLibId);
1204
1205pub struct ThreadRegistryEntry {
1211 pub state: Rc<RefCell<LuaState>>,
1216 pub value: GcRef<lua_types::value::LuaThread>,
1219}
1220
1221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1226pub struct ExternalRootKey {
1227 index: usize,
1228 generation: u64,
1229}
1230
1231#[derive(Debug)]
1232struct ExternalRootSlot {
1233 value: Option<LuaValue>,
1234 generation: u64,
1235}
1236
1237#[derive(Debug, Default)]
1243pub struct ExternalRootSet {
1244 slots: Vec<ExternalRootSlot>,
1245 free: Vec<usize>,
1246 live: usize,
1247}
1248
1249impl ExternalRootSet {
1250 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1251 if let Some(index) = self.free.pop() {
1252 let slot = &mut self.slots[index];
1253 debug_assert!(slot.value.is_none(), "free external-root slot is occupied");
1254 slot.generation = slot.generation.wrapping_add(1).max(1);
1255 slot.value = Some(value);
1256 self.live += 1;
1257 ExternalRootKey {
1258 index,
1259 generation: slot.generation,
1260 }
1261 } else {
1262 let index = self.slots.len();
1263 self.slots.push(ExternalRootSlot {
1264 value: Some(value),
1265 generation: 1,
1266 });
1267 self.live += 1;
1268 ExternalRootKey {
1269 index,
1270 generation: 1,
1271 }
1272 }
1273 }
1274
1275 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1276 let slot = self.slots.get(key.index)?;
1277 if slot.generation == key.generation {
1278 slot.value.as_ref()
1279 } else {
1280 None
1281 }
1282 }
1283
1284 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1285 let slot = self.slots.get_mut(key.index)?;
1286 if slot.generation != key.generation || slot.value.is_none() {
1287 return None;
1288 }
1289 slot.value.replace(value)
1290 }
1291
1292 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1293 let slot = self.slots.get_mut(key.index)?;
1294 if slot.generation != key.generation {
1295 return None;
1296 }
1297 let old = slot.value.take()?;
1298 self.free.push(key.index);
1299 self.live -= 1;
1300 Some(old)
1301 }
1302
1303 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1304 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1305 }
1306
1307 pub fn len(&self) -> usize {
1308 self.live
1309 }
1310
1311 pub fn is_empty(&self) -> bool {
1312 self.live == 0
1313 }
1314
1315 pub fn vacant_len(&self) -> usize {
1316 self.free.len()
1317 }
1318}
1319
1320pub struct GlobalState {
1326 pub parser_hook: Option<ParserHook>,
1332
1333 pub cli_argv: Option<Vec<Vec<u8>>>,
1340
1341 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1345
1346 pub lua_version: lua_types::LuaVersion,
1353
1354 pub file_loader_hook: Option<FileLoaderHook>,
1359
1360 pub file_open_hook: Option<FileOpenHook>,
1365
1366 pub stdout_hook: Option<OutputHook>,
1370
1371 pub stderr_hook: Option<OutputHook>,
1373
1374 pub stdin_hook: Option<InputHook>,
1377
1378 pub env_hook: Option<EnvHook>,
1380
1381 pub unix_time_hook: Option<UnixTimeHook>,
1384
1385 pub cpu_clock_hook: Option<CpuClockHook>,
1388
1389 pub local_offset_hook: Option<LocalOffsetHook>,
1394
1395 pub entropy_hook: Option<EntropyHook>,
1398
1399 pub temp_name_hook: Option<TempNameHook>,
1401
1402 pub popen_hook: Option<PopenHook>,
1407
1408 pub file_remove_hook: Option<FileRemoveHook>,
1411
1412 pub file_rename_hook: Option<FileRenameHook>,
1415
1416 pub os_execute_hook: Option<OsExecuteHook>,
1420
1421 pub dynlib_load_hook: Option<DynLibLoadHook>,
1426
1427 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1431
1432 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1436
1437 pub sandbox: SandboxLimits,
1440
1441 pub gc_debt: isize,
1443
1444 pub gc_estimate: usize,
1445
1446 pub lastatomic: usize,
1448
1449 pub strt: StringPool,
1451
1452 pub l_registry: LuaValue,
1454
1455 pub external_roots: ExternalRootSet,
1458
1459 pub globals: LuaValue,
1466 pub loaded: LuaValue,
1467
1468 pub nilvalue: LuaValue,
1472
1473 pub seed: u32,
1475
1476 pub currentwhite: u8,
1478
1479 pub gcstate: u8,
1480
1481 pub gckind: u8,
1482
1483 pub gcstopem: bool,
1484
1485 pub genminormul: u8,
1487
1488 pub genmajormul: u8,
1489
1490 pub gcstp: u8,
1491
1492 pub gcemergency: bool,
1493
1494 pub gcpause: u8,
1496
1497 pub gcstepmul: u8,
1499
1500 pub gcstepsize: u8,
1501
1502 pub gc55_params: [i64; 6],
1511
1512 pub sweepgc_cursor: usize,
1517
1518 pub weak_tables_registry: lua_gc::WeakRegistry<WeakTableEntry>,
1527
1528 pub finalizers: lua_gc::FinalizerRegistry<FinalizerObject>,
1531
1532 pub gc_finalizer_error: Option<LuaValue>,
1543
1544 pub twups: Vec<GcRef<LuaState>>,
1554
1555 pub panic: Option<LuaCFunction>,
1557
1558 pub mainthread: Option<GcRef<LuaState>>,
1561
1562 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1575
1576 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1581
1582 pub current_thread_id: u64,
1587
1588 pub closing_thread_id: Option<u64>,
1591
1592 pub main_thread_id: u64,
1595
1596 pub next_thread_id: u64,
1599
1600 pub memerrmsg: GcRef<LuaString>,
1602
1603 pub tmname: Vec<GcRef<LuaString>>,
1606
1607 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1609
1610 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1612
1613 pub interned_lt: InternedStringMap,
1619
1620 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1622
1623 pub warn_mode: WarnMode,
1628
1629 pub test_warn_enabled: bool,
1634 pub test_warn_on: bool,
1635 pub test_warn_mode: TestWarnMode,
1636 pub test_warn_last_to_cont: bool,
1637 pub test_warn_buffer: Vec<u8>,
1638
1639 pub c_functions: Vec<LuaCallable>,
1644
1645 pub heap: lua_gc::Heap,
1650
1651 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1665
1666 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1680
1681 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1687}
1688
1689const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1692
1693pub const SANDBOX_TRIP_NONE: u8 = 0;
1695pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1697pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1699
1700#[derive(Default)]
1707pub struct SandboxLimits {
1708 pub interval: std::cell::Cell<i32>,
1710 pub instr_limited: std::cell::Cell<bool>,
1712 pub instr_remaining: std::cell::Cell<u64>,
1714 pub instr_limit: std::cell::Cell<u64>,
1716 pub mem_limit: std::cell::Cell<Option<usize>>,
1718 pub tripped: std::cell::Cell<u8>,
1720 pub aborting: std::cell::Cell<bool>,
1725}
1726
1727impl GlobalState {
1728 pub fn sandbox_active(&self) -> bool {
1730 self.sandbox.interval.get() != 0
1731 }
1732
1733 pub fn total_bytes(&self) -> usize {
1738 self.heap.bytes_used().max(1)
1739 }
1740
1741 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1746 self.threads.get(&id)
1747 }
1748
1749 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1753 if id == self.main_thread_id {
1754 Some(self.main_thread_value.clone())
1755 } else {
1756 self.threads.get(&id).map(|e| e.value.clone())
1757 }
1758 }
1759
1760 pub fn is_complete(&self) -> bool {
1767 matches!(self.nilvalue, LuaValue::Nil)
1768 }
1769
1770 pub fn current_white(&self) -> u8 {
1778 self.currentwhite
1779 }
1780
1781 pub fn other_white(&self) -> u8 {
1785 self.currentwhite ^ 0x03
1786 }
1787
1788 pub fn is_gen_mode(&self) -> bool {
1792 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1793 }
1794
1795 pub fn gc_running(&self) -> bool {
1799 self.gcstp == 0
1800 }
1801
1802 pub fn keep_invariant(&self) -> bool {
1806 self.heap.gc_state().is_invariant()
1807 }
1808
1809 pub fn is_sweep_phase(&self) -> bool {
1813 self.heap.gc_state().is_sweep()
1814 }
1815
1816 pub fn gc_debt(&self) -> isize {
1818 self.gc_debt
1819 }
1820 pub fn set_gc_debt(&mut self, d: isize) {
1821 self.gc_debt = d;
1822 }
1823 pub fn gc_at_pause(&self) -> bool {
1824 self.heap.gc_state().is_pause()
1825 }
1826 fn get_gc_param(p: u8) -> i32 {
1827 (p as i32) * 4
1828 }
1829 fn set_gc_param_slot(slot: &mut u8, p: i32) {
1830 *slot = (p / 4) as u8;
1831 }
1832 pub fn gc_pause_param(&self) -> i32 {
1833 Self::get_gc_param(self.gcpause)
1834 }
1835 pub fn set_gc_pause_param(&mut self, p: i32) {
1836 Self::set_gc_param_slot(&mut self.gcpause, p);
1837 }
1838 pub fn gc_stepmul_param(&self) -> i32 {
1839 Self::get_gc_param(self.gcstepmul)
1840 }
1841 pub fn set_gc_stepmul_param(&mut self, p: i32) {
1842 Self::set_gc_param_slot(&mut self.gcstepmul, p);
1843 }
1844 pub fn gc_genmajormul_param(&self) -> i32 {
1845 Self::get_gc_param(self.genmajormul)
1846 }
1847 pub fn set_gc_genmajormul(&mut self, p: i32) {
1848 Self::set_gc_param_slot(&mut self.genmajormul, p);
1849 }
1850 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1854 let old = self.gc55_params[idx];
1855 if value >= 0 {
1856 self.gc55_params[idx] = value;
1857 }
1858 old
1859 }
1860 pub fn gc_stop_flags(&self) -> u8 {
1861 self.gcstp
1862 }
1863 pub fn set_gc_stop_flags(&mut self, f: u8) {
1864 self.gcstp = f;
1865 }
1866 pub fn stop_gc_internal(&mut self) -> u8 {
1867 let old = self.gcstp;
1868 self.gcstp |= GCSTPGC;
1869 old
1870 }
1871 pub fn set_gc_stop_user(&mut self) {
1872 self.gcstp = GCSTPUSR;
1874 }
1875 pub fn clear_gc_stop(&mut self) {
1876 self.gcstp = 0;
1877 }
1878 pub fn is_gc_running(&self) -> bool {
1879 self.gcstp == 0
1880 }
1881 pub fn is_gc_stopped_internally(&self) -> bool {
1886 (self.gcstp & GCSTPGC) != 0
1887 }
1888
1889 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1899 self.tmname.get(tm.tm_index()).cloned()
1900 }
1901}
1902
1903pub trait TmIndex: Copy {
1909 fn tm_index(self) -> usize;
1910}
1911impl TmIndex for lua_types::tagmethod::TagMethod {
1912 fn tm_index(self) -> usize {
1913 self as u8 as usize
1914 }
1915}
1916impl TmIndex for crate::tagmethods::TagMethod {
1917 fn tm_index(self) -> usize {
1918 self as u8 as usize
1919 }
1920}
1921impl TmIndex for usize {
1922 fn tm_index(self) -> usize {
1923 self
1924 }
1925}
1926impl TmIndex for u8 {
1927 fn tm_index(self) -> usize {
1928 self as usize
1929 }
1930}
1931
1932use lua_types::tagmethod::TagMethod;
1933
1934pub struct LuaState {
1944 pub status: u8,
1948
1949 pub allowhook: bool,
1951
1952 pub nci: u32,
1954
1955 pub top: StackIdx,
1959
1960 pub stack_last: StackIdx,
1962
1963 pub stack: Vec<StackValue>,
1965
1966 pub ci: CallInfoIdx,
1970
1971 pub call_info: Vec<CallInfo>,
1974
1975 pub openupval: Vec<GcRef<UpVal>>,
1979
1980 pub tbclist: Vec<StackIdx>,
1982
1983 pub(crate) global: Rc<RefCell<GlobalState>>,
1988
1989 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
1993
1994 pub hookmask: u8,
1996
1997 pub basehookcount: i32,
1999
2000 pub hookcount: i32,
2002
2003 pub errfunc: isize,
2010
2011 pub n_ccalls: u32,
2015
2016 pub oldpc: u32,
2020
2021 pub marked: u8,
2025
2026 pub cached_thread_id: u64,
2042
2043 pub gc_check_needed: bool,
2048}
2049
2050impl LuaState {
2051 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
2059 self.global.borrow()
2060 }
2061
2062 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
2066 self.global.borrow_mut()
2067 }
2068
2069 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
2073 Rc::clone(&self.global)
2074 }
2075
2076 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
2078 {
2079 let mut g = self.global_mut();
2080 g.test_warn_enabled = true;
2081 g.test_warn_on = false;
2082 g.test_warn_mode = TestWarnMode::Normal;
2083 g.test_warn_last_to_cont = false;
2084 g.test_warn_buffer.clear();
2085 }
2086 self.push(LuaValue::Bool(false));
2087 crate::api::set_global(self, b"_WARN")
2088 }
2089
2090 pub fn c_calls(&self) -> u32 {
2094 self.n_ccalls & 0xffff
2095 }
2096
2097 pub fn inc_nny(&mut self) {
2101 self.n_ccalls += 0x10000;
2102 }
2103
2104 pub fn dec_nny(&mut self) {
2108 self.n_ccalls -= 0x10000;
2109 }
2110
2111 pub fn is_yieldable(&self) -> bool {
2115 (self.n_ccalls & 0xffff0000) == 0
2116 }
2117
2118 pub fn reset_hook_count(&mut self) {
2122 self.hookcount = self.basehookcount;
2123 }
2124
2125 pub fn install_sandbox_limits(
2134 &mut self,
2135 interval: i32,
2136 instr_limit: Option<u64>,
2137 mem_limit: Option<usize>,
2138 ) {
2139 let interval = interval.max(1);
2140 {
2141 let g = self.global();
2142 g.sandbox.interval.set(interval);
2143 g.sandbox.instr_limited.set(instr_limit.is_some());
2144 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
2145 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
2146 g.sandbox.mem_limit.set(mem_limit);
2147 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2148 }
2149 self.hookmask |= SANDBOX_COUNT_MASK;
2150 self.basehookcount = interval;
2151 self.hookcount = interval;
2152 crate::debug::arm_traps(self);
2153 }
2154
2155 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
2160 let interval = self.global().sandbox.interval.get();
2161 self.sandbox_charge(interval as u64)
2162 }
2163
2164 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
2173 let g = self.global();
2174 if g.sandbox.interval.get() == 0 {
2175 return None;
2176 }
2177 if g.sandbox.instr_limited.get() {
2178 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
2179 g.sandbox.instr_remaining.set(rem);
2180 if rem == 0 {
2181 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
2182 g.sandbox.aborting.set(true);
2183 return Some(LuaError::runtime(format_args!(
2184 "sandbox: instruction budget exhausted"
2185 )));
2186 }
2187 }
2188 if let Some(limit) = g.sandbox.mem_limit.get() {
2189 if g.total_bytes() > limit {
2190 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2191 g.sandbox.aborting.set(true);
2192 return Some(LuaError::runtime(format_args!(
2193 "sandbox: memory limit exceeded"
2194 )));
2195 }
2196 }
2197 None
2198 }
2199
2200 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
2208 let g = self.global();
2209 if g.sandbox.interval.get() == 0 {
2210 return None;
2211 }
2212 if let Some(limit) = g.sandbox.mem_limit.get() {
2213 let projected = g.total_bytes().saturating_add(additional);
2214 if projected > limit {
2215 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2216 g.sandbox.aborting.set(true);
2217 return Some(LuaError::runtime(format_args!(
2218 "sandbox: memory limit exceeded"
2219 )));
2220 }
2221 }
2222 None
2223 }
2224
2225 pub fn sandbox_match_step_limit(&self) -> u64 {
2230 let g = self.global();
2231 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
2232 g.sandbox.instr_remaining.get()
2233 } else {
2234 0
2235 }
2236 }
2237
2238 pub fn sandbox_aborting(&self) -> bool {
2242 self.global().sandbox.aborting.get()
2243 }
2244
2245 pub fn sandbox_instr_limited(&self) -> bool {
2247 self.global().sandbox.instr_limited.get()
2248 }
2249
2250 pub fn sandbox_instr_remaining(&self) -> u64 {
2253 self.global().sandbox.instr_remaining.get()
2254 }
2255
2256 pub fn sandbox_instr_limit(&self) -> u64 {
2258 self.global().sandbox.instr_limit.get()
2259 }
2260
2261 pub fn sandbox_tripped_code(&self) -> u8 {
2263 self.global().sandbox.tripped.get()
2264 }
2265
2266 pub fn sandbox_reset(&self) {
2269 let g = self.global();
2270 if g.sandbox.instr_limited.get() {
2271 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2272 }
2273 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2274 g.sandbox.aborting.set(false);
2275 }
2276
2277 pub fn stack_size(&self) -> usize {
2281 self.stack_last.0 as usize
2282 }
2283
2284 #[inline(always)]
2288 pub fn push(&mut self, val: LuaValue) {
2289 let top = self.top.0 as usize;
2290 if top < self.stack.len() {
2291 self.stack[top] = StackValue { val, tbc_delta: 0 };
2292 } else {
2293 self.stack.push(StackValue { val, tbc_delta: 0 });
2294 }
2295 self.top = StackIdx(self.top.0 + 1);
2296 }
2297
2298 #[inline(always)]
2301 pub fn pop(&mut self) -> LuaValue {
2302 if self.top.0 == 0 {
2303 return LuaValue::Nil;
2304 }
2305 self.top = StackIdx(self.top.0 - 1);
2306 self.stack[self.top.0 as usize].val.clone()
2307 }
2308
2309 #[inline(always)]
2313 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2314 &self.stack[idx.0 as usize].val
2315 }
2316
2317 #[inline(always)]
2319 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2320 self.stack[idx.0 as usize].val = val;
2321 }
2322
2323 pub fn gc(&mut self) -> GcHandle<'_> {
2330 GcHandle { _state: self }
2331 }
2332
2333 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2335 self.global_mut().external_roots.insert(value)
2336 }
2337
2338 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2340 self.global().external_roots.get(key).cloned()
2341 }
2342
2343 pub fn external_replace_root(
2345 &mut self,
2346 key: ExternalRootKey,
2347 value: LuaValue,
2348 ) -> Option<LuaValue> {
2349 self.global_mut().external_roots.replace(key, value)
2350 }
2351
2352 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2354 self.global_mut().external_roots.remove(key)
2355 }
2356
2357 pub fn try_external_unroot_value(
2360 &mut self,
2361 key: ExternalRootKey,
2362 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2363 self.global
2364 .try_borrow_mut()
2365 .map(|mut global| global.external_roots.remove(key))
2366 }
2367
2368 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2372 self.mark_gc_check_needed();
2374 GcRef::new(LuaTable::placeholder())
2375 }
2376
2377 pub fn new_table_with_sizes(
2382 &mut self,
2383 array_size: u32,
2384 hash_size: u32,
2385 ) -> Result<GcRef<LuaTable>, LuaError> {
2386 self.mark_gc_check_needed();
2387 let t = GcRef::new(LuaTable::placeholder());
2388 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2389 Ok(t)
2390 }
2391
2392 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2403 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2404 let mut inserted = false;
2405 let interned = {
2406 let key = bytes.to_vec().into_boxed_slice();
2407 let mut global = self.global_mut();
2408 match global.interned_lt.entry(key) {
2409 Entry::Occupied(existing) => existing.get().clone(),
2410 Entry::Vacant(vacant) => {
2411 let new_ref = GcRef::new(LuaString::from_bytes(vacant.key().to_vec()));
2412 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2413 vacant.insert(new_ref.clone());
2414 inserted = true;
2415 new_ref
2416 }
2417 }
2418 };
2419 if inserted {
2420 self.mark_gc_check_needed();
2421 }
2422 Ok(interned)
2423 } else {
2424 self.mark_gc_check_needed();
2425 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2426 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2427 Ok(new_ref)
2428 }
2429 }
2430
2431 #[inline(always)]
2433 pub fn top_idx(&self) -> StackIdx {
2434 self.top
2435 }
2436}
2437
2438impl LuaState {
2447 #[inline(always)]
2448 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2449 let i: StackIdx = idx.into().0;
2450 match self.stack.get(i.0 as usize) {
2451 Some(slot) => slot.val.clone(),
2452 None => LuaValue::Nil,
2453 }
2454 }
2455 #[inline(always)]
2456 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2457 let i: StackIdx = idx.into().0;
2458 self.stack[i.0 as usize].val = v;
2459 }
2460
2461 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2467 if end.0 <= start.0 {
2468 return;
2469 }
2470 let end_u = end.0 as usize;
2471 if end_u > self.stack.len() {
2472 self.stack.resize_with(end_u, StackValue::default);
2473 }
2474 for i in start.0..end.0 {
2475 self.stack[i as usize].val = LuaValue::Nil;
2476 self.stack[i as usize].tbc_delta = 0;
2477 }
2478 }
2479 #[inline(always)]
2487 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2488 let i: StackIdx = idx.into().0;
2489 match self.stack.get(i.0 as usize) {
2490 Some(slot) => match &slot.val {
2491 LuaValue::Int(v) => Some(*v),
2492 _ => None,
2493 },
2494 None => None,
2495 }
2496 }
2497 #[inline(always)]
2504 pub fn get_int_pair_at(
2505 &self,
2506 rb: impl Into<StackIdxConv>,
2507 rc: impl Into<StackIdxConv>,
2508 ) -> Option<(i64, i64)> {
2509 let rb: StackIdx = rb.into().0;
2510 let rc: StackIdx = rc.into().0;
2511 match (self.stack[rb.0 as usize].val, self.stack[rc.0 as usize].val) {
2512 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2513 _ => None,
2514 }
2515 }
2516 #[inline(always)]
2521 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2522 let i: StackIdx = idx.into().0;
2523 match self.stack.get(i.0 as usize) {
2524 Some(slot) => match &slot.val {
2525 LuaValue::Float(f) => Some(*f),
2526 LuaValue::Int(v) => Some(*v as f64),
2527 _ => None,
2528 },
2529 None => None,
2530 }
2531 }
2532 #[inline(always)]
2537 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2538 let i: StackIdx = idx.into().0;
2539 match self.stack.get(i.0 as usize) {
2540 Some(slot) => match &slot.val {
2541 LuaValue::Float(f) => Some(*f),
2542 _ => None,
2543 },
2544 None => None,
2545 }
2546 }
2547 #[inline(always)]
2552 pub fn get_num_pair_at(
2553 &self,
2554 rb: impl Into<StackIdxConv>,
2555 rc: impl Into<StackIdxConv>,
2556 ) -> Option<(f64, f64)> {
2557 let rb: StackIdx = rb.into().0;
2558 let rc: StackIdx = rc.into().0;
2559 match (self.stack[rb.0 as usize].val, self.stack[rc.0 as usize].val) {
2560 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2561 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2562 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2563 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2564 _ => None,
2565 }
2566 }
2567 #[inline(always)]
2580 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2581 let new_top: StackIdx = idx.into().0;
2582 let new_top_u = new_top.0 as usize;
2583 if new_top_u > self.stack.len() {
2584 self.stack.resize_with(new_top_u, StackValue::default);
2585 }
2586 self.top = new_top;
2587 }
2588 #[inline(always)]
2594 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2595 let new_top: StackIdx = idx.into().0;
2596 self.top = new_top;
2597 }
2598 #[inline(always)]
2601 pub fn dec_top(&mut self) {
2602 if self.top.0 > 0 {
2603 self.top = StackIdx(self.top.0 - 1);
2604 }
2605 }
2606 #[inline(always)]
2607 pub fn pop_n(&mut self, n: usize) {
2608 let cur = self.top.0 as usize;
2609 let new = cur.saturating_sub(n);
2610 self.top = StackIdx(new as u32);
2611 }
2612 #[inline(always)]
2615 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2616 let i: StackIdx = idx.into().0;
2617 match self.stack.get(i.0 as usize) {
2618 Some(slot) => slot.val.clone(),
2619 None => LuaValue::Nil,
2620 }
2621 }
2622 #[inline(always)]
2626 pub fn peek_top(&mut self) -> LuaValue {
2627 if self.top.0 == 0 {
2628 return LuaValue::Nil;
2629 }
2630 self.stack[(self.top.0 - 1) as usize].val.clone()
2631 }
2632 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2637 match self.peek_top() {
2638 LuaValue::Str(s) => s,
2639 _ => panic!("peek_string_at_top: top of stack is not a string"),
2640 }
2641 }
2642 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2645 let i: StackIdx = idx.into().0;
2646 &mut self.stack[i.0 as usize].val
2647 }
2648 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2651 let i: StackIdx = idx.into().0;
2652 let slot = i.0 as usize;
2653 if slot < self.stack.len() {
2654 self.stack[slot].val = LuaValue::Nil;
2655 }
2656 }
2657 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2665 self.stack.resize_with(size, StackValue::default);
2666 Ok(())
2667 }
2668 pub fn stack_available(&mut self) -> usize {
2669 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2670 }
2671 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2672 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2673 if free <= n {
2674 self.grow_stack(n, true)?;
2675 }
2676 Ok(())
2677 }
2678 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2687 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2688 }
2689
2690 #[inline(always)]
2691 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo {
2692 &self.call_info[idx.as_usize()]
2693 }
2694 #[inline(always)]
2695 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo {
2696 &mut self.call_info[idx.as_usize()]
2697 }
2698 #[inline(always)]
2699 pub fn current_call_info(&self) -> &CallInfo {
2700 &self.call_info[self.ci.as_usize()]
2701 }
2702 #[inline(always)]
2703 pub fn current_call_info_mut(&mut self) -> &mut CallInfo {
2704 let i = self.ci.as_usize();
2705 &mut self.call_info[i]
2706 }
2707 #[inline(always)]
2708 pub fn current_ci_idx(&self) -> CallInfoIdx {
2709 self.ci
2710 }
2711 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> {
2712 &mut self.call_info
2713 }
2714 #[inline(always)]
2715 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2716 match self.call_info[self.ci.as_usize()].next {
2717 Some(idx) => Ok(idx),
2718 None => Ok(extend_ci(self)),
2719 }
2720 }
2721 #[inline(always)]
2722 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> {
2723 self.call_info[idx.as_usize()].previous
2724 }
2725 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2726 self.call_info[idx.as_usize()]
2727 .previous
2728 .map(|p| &self.call_info[p.as_usize()])
2729 }
2730 #[inline(always)]
2731 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool {
2732 idx.as_usize() == 0
2733 }
2734 #[inline(always)]
2735 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool {
2736 idx == self.ci
2737 }
2738 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2739 let next = self.call_info[idx.as_usize()]
2740 .next
2741 .expect("ci_next_func: no next CallInfo");
2742 self.call_info[next.as_usize()].func
2743 }
2744 #[inline(always)]
2745 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx {
2746 self.call_info[idx.as_usize()].top
2747 }
2748 #[inline(always)]
2749 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2750 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2751 trap
2752 } else {
2753 false
2754 }
2755 }
2756 #[inline(always)]
2757 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 {
2758 self.call_info[idx.as_usize()].saved_pc()
2759 }
2760 #[inline(always)]
2761 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2762 self.call_info[idx.as_usize()].set_saved_pc(pc);
2763 }
2764 #[inline(always)]
2765 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2766 self.ci = self.call_info[idx.as_usize()]
2767 .previous
2768 .expect("set_ci_previous: returning frame has no previous CallInfo");
2769 }
2770 #[inline(always)]
2771 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> {
2772 self.call_info[idx.as_usize()].previous
2773 }
2774 #[inline(always)]
2775 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2776 let ci = &mut self.call_info[idx.as_usize()];
2777 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2778 }
2779 #[inline(always)]
2780 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx {
2781 self.call_info[idx.as_usize()].func + 1
2782 }
2783 #[inline(always)]
2784 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2785 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2786 }
2787 #[inline(always)]
2788 pub fn ci_lua_closure(
2789 &self,
2790 idx: CallInfoIdx,
2791 ) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2792 let func_idx = self.call_info[idx.as_usize()].func;
2793 match self.stack.get(func_idx.0 as usize).map(|slot| slot.val) {
2794 Some(LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl))) => Some(cl),
2795 _ => None,
2796 }
2797 }
2798 #[inline(always)]
2799 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2800 self.call_info[idx.as_usize()].nextra_args()
2801 }
2802 #[inline(always)]
2803 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2804 self.call_info[idx.as_usize()].u2.value
2805 }
2806 #[inline(always)]
2807 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2808 self.call_info[idx.as_usize()].u2.value = n;
2809 }
2810 #[inline(always)]
2811 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 {
2812 self.call_info[idx.as_usize()].nresults as i32
2813 }
2814 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2815 let pc = self.call_info[idx.as_usize()].saved_pc();
2816 let cl = self
2817 .ci_lua_closure(idx)
2818 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2819 cl.proto.code[(pc - 1) as usize]
2820 }
2821 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2822 let pc = self.call_info[idx.as_usize()].saved_pc();
2823 let cl = self
2824 .ci_lua_closure(idx)
2825 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2826 cl.proto.code[(pc - 2) as usize]
2827 }
2828 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2829 let pc = self.call_info[idx.as_usize()].saved_pc();
2830 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2831 }
2832 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2833 let pc = self.call_info[idx.as_usize()].saved_pc();
2834 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2835 }
2836 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2837 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2838 }
2839 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2840 self.call_info[idx.as_usize()].u2.value
2841 }
2842 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2843 self.call_info[idx.as_usize()].u2.value
2844 }
2845 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2846 self.call_info[idx.as_usize()].u2.value
2847 }
2848 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2849 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2850 match self.ci_lua_closure(idx) {
2851 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2852 None => (false, nextraargs, 0),
2853 }
2854 }
2855 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2856 self.ci_lua_closure(idx)
2857 .map(|cl| cl.proto.numparams)
2858 .unwrap_or(0)
2859 }
2860 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2861 self.call_info[idx.as_usize()].u2.value = n;
2862 }
2863 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2864 self.call_info[idx.as_usize()].u2.value = n;
2865 }
2866 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2867 let ci = &mut self.call_info[idx.as_usize()];
2868 ci.u2.ftransfer = ftransfer;
2869 ci.u2.ntransfer = ntransfer;
2870 }
2871 pub fn shrink_ci(&mut self) {
2872 shrink_ci(self)
2873 }
2874 pub fn check_c_stack(&mut self) -> Result<(), LuaError> {
2875 check_c_stack(self)
2876 }
2877
2878 pub fn status(&mut self) -> LuaStatus {
2879 LuaStatus::from_raw(self.status as i32)
2880 }
2881 pub fn errfunc(&mut self) -> isize {
2882 self.errfunc
2883 }
2884 pub fn old_pc(&mut self) -> u32 {
2885 self.oldpc
2886 }
2887 pub fn set_old_pc(&mut self, pc: u32) {
2888 self.oldpc = pc;
2889 }
2890 pub fn set_oldpc(&mut self, pc: u32) {
2891 self.oldpc = pc;
2892 }
2893 pub fn _hook_call_noargs(&mut self) {}
2894 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2895 self.hook.as_ref()
2896 }
2897 pub fn has_hook(&mut self) -> bool {
2898 self.hook.is_some()
2899 }
2900 pub fn hook_count(&mut self) -> i32 {
2901 self.hookcount
2902 }
2903 pub fn set_hook_count(&mut self, n: i32) {
2904 self.hookcount = n;
2905 }
2906 pub fn hook_mask(&self) -> u8 {
2907 self.hookmask
2908 }
2909 pub fn set_hook_mask(&mut self, m: u8) {
2910 self.hookmask = m;
2911 }
2912 pub fn base_hook_count(&self) -> i32 {
2913 self.basehookcount
2914 }
2915 pub fn set_base_hook_count(&mut self, n: i32) {
2916 self.basehookcount = n;
2917 }
2918 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2919 self.hook = h;
2920 }
2921 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2922 crate::do_::hook(self, event, line, 0, 0)
2923 }
2924
2925 pub fn registry_value(&self) -> LuaValue {
2926 self.global().l_registry.clone()
2927 }
2928 pub fn registry_get(&self, key: usize) -> LuaValue {
2929 let reg = self.global().l_registry.clone();
2930 match reg {
2931 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2932 _ => LuaValue::Nil,
2933 }
2934 }
2935
2936 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2937 self.intern_or_create_str(bytes)
2938 }
2939
2940 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2952 self.mark_gc_check_needed();
2953 GcRef::new(LuaProto::placeholder())
2954 }
2955
2956 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2958 self.mark_gc_check_needed();
2959 let mut upvals = Vec::with_capacity(nupvals);
2960 for _ in 0..nupvals {
2961 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
2962 }
2963 let closure = GcRef::new(LuaClosureLua { proto, upvals });
2964 closure.account_buffer(closure.buffer_bytes() as isize);
2965 closure
2966 }
2967
2968 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
2970 self.mark_gc_check_needed();
2971 GcRef::new(UpVal::closed(v))
2972 }
2973
2974 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
2976 self.mark_gc_check_needed();
2977 GcRef::new(UpVal::open(thread_id, level))
2978 }
2979 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2986 self.intern_str(bytes)
2987 }
2988 pub fn new_userdata(
2989 &mut self,
2990 _size: usize,
2991 _nuvalue: usize,
2992 ) -> Result<GcRef<LuaUserData>, LuaError> {
2993 Err(LuaError::runtime(format_args!(
2994 "new_userdata not implemented in this Phase-B build; use new_userdata_typed instead"
2995 )))
2996 }
2997 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
2998 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
2999 }
3000 pub fn push_closure(
3001 &mut self,
3002 proto_idx: usize,
3003 ci: CallInfoIdx,
3004 base: StackIdx,
3005 ra: StackIdx,
3006 ) -> Result<(), LuaError> {
3007 let parent_cl = self
3008 .ci_lua_closure(ci)
3009 .expect("push_closure: current frame is not a Lua closure");
3010 let child_proto = parent_cl.proto.p[proto_idx].clone();
3011 let nup = child_proto.upvalues.len();
3012 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
3013 for i in 0..nup {
3014 let desc = &child_proto.upvalues[i];
3015 let uv = if desc.instack {
3016 let level = base + desc.idx as i32;
3017 crate::func::find_upval(self, level)
3018 } else {
3019 parent_cl.upval(desc.idx as usize)
3020 };
3021 upvals.push(std::cell::Cell::new(uv));
3022 }
3023 let cache_enabled = matches!(
3027 self.global().lua_version,
3028 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
3029 );
3030 if cache_enabled {
3031 if let Some(cached) = child_proto.cache.borrow().as_ref() {
3032 if cached.upvals.len() == nup
3033 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
3034 {
3035 let reused = cached.clone();
3036 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
3037 return Ok(());
3038 }
3039 }
3040 }
3041 self.mark_gc_check_needed();
3044 let new_cl = GcRef::new(LuaClosureLua {
3045 proto: child_proto.clone(),
3046 upvals,
3047 });
3048 new_cl.account_buffer(new_cl.buffer_bytes() as isize);
3049 if cache_enabled {
3050 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
3051 }
3052 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
3053 Ok(())
3054 }
3055 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
3056 crate::func::new_tbc_upval(self, idx)
3057 }
3058
3059 #[inline(always)]
3080 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
3081 let uv = cl.upval(n);
3082 let (thread_id, idx) = match uv.try_open_payload() {
3083 Some(p) => p,
3084 None => return uv.closed_value(),
3085 };
3086 let current = self.cached_thread_id;
3087 let tid = thread_id as u64;
3088 if tid == current {
3089 return self.stack[idx.0 as usize].val;
3090 }
3091 self.upvalue_get_cross_thread(tid, idx)
3092 }
3093
3094 #[cold]
3095 #[inline(never)]
3096 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
3097 let entry_rc = {
3098 let g = self.global();
3099 g.threads.get(&tid).map(|e| e.state.clone())
3100 };
3101 if let Some(rc) = entry_rc {
3102 if let Ok(home_state) = rc.try_borrow() {
3103 return home_state.get_at(idx);
3104 }
3105 }
3106 let g = self.global();
3107 match g.cross_thread_upvals.get(&(tid, idx)) {
3108 Some(v) => *v,
3109 None => LuaValue::Nil,
3110 }
3111 }
3112 #[inline(always)]
3121 pub fn upvalue_set(
3122 &mut self,
3123 cl: &GcRef<LuaClosureLua>,
3124 n: usize,
3125 val: LuaValue,
3126 ) -> Result<(), LuaError> {
3127 let uv = cl.upval(n);
3128 match uv.try_open_payload() {
3129 Some((thread_id, idx)) => {
3130 let tid = thread_id as u64;
3131 let current = self.cached_thread_id;
3132 if tid == current {
3133 self.stack[idx.0 as usize].val = val;
3134 } else {
3135 self.upvalue_set_cross_thread(tid, idx, val)?;
3136 }
3137 }
3138 None => {
3139 uv.set_closed_value(val);
3140 }
3141 }
3142 if val.is_collectable() {
3143 self.gc_barrier_upval(&uv, &val);
3144 }
3145 Ok(())
3146 }
3147
3148 #[cold]
3149 #[inline(never)]
3150 fn upvalue_set_cross_thread(
3151 &mut self,
3152 tid: u64,
3153 idx: StackIdx,
3154 val: LuaValue,
3155 ) -> Result<(), LuaError> {
3156 let entry_rc = {
3157 let g = self.global();
3158 g.threads.get(&tid).map(|e| e.state.clone())
3159 };
3160 if let Some(rc) = entry_rc {
3161 if let Ok(mut home_state) = rc.try_borrow_mut() {
3162 home_state.set_at(idx, val);
3163 return Ok(());
3164 }
3165 }
3166 let mut g = self.global_mut();
3167 g.cross_thread_upvals.insert((tid, idx), val);
3168 Ok(())
3169 }
3170
3171 pub fn protected_call_raw(
3172 &mut self,
3173 func: StackIdx,
3174 nresults: i32,
3175 errfunc: StackIdx,
3176 ) -> Result<(), LuaError> {
3177 let ef = errfunc.0 as isize;
3178 let status = crate::do_::pcall(self, |s| s.call_no_yield(func, nresults), func, ef);
3179 match status {
3180 LuaStatus::Ok => Ok(()),
3181 LuaStatus::ErrSyntax => {
3182 let err_val = self.get_at(func);
3183 self.set_top(func);
3184 Err(LuaError::Syntax(err_val))
3185 }
3186 LuaStatus::Yield => {
3187 self.set_top(func);
3188 Err(LuaError::Yield)
3189 }
3190 _ => {
3191 let err_val = self.get_at(func);
3192 self.set_top(func);
3193 Err(LuaError::Runtime(err_val))
3194 }
3195 }
3196 }
3197 pub fn protected_parser(
3198 &mut self,
3199 z: crate::zio::ZIO,
3200 name: &[u8],
3201 mode: Option<&[u8]>,
3202 ) -> LuaStatus {
3203 crate::do_::protected_parser(self, z, name, mode)
3204 }
3205 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3206 crate::do_::call(self, func, nresults)
3207 }
3208 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3209 crate::do_::callnoyield(self, func, nresults)
3210 }
3211 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3212 crate::do_::callnoyield(self, func, nresults)
3213 }
3214 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
3215 crate::do_::call(self, func, nresults)
3216 }
3217 #[inline(always)]
3218 pub fn precall(
3219 &mut self,
3220 func: StackIdx,
3221 nresults: i32,
3222 ) -> Result<Option<CallInfoIdx>, LuaError> {
3223 crate::do_::precall(self, func, nresults)
3224 }
3225 #[inline(always)]
3226 pub fn pretailcall(
3227 &mut self,
3228 ci: CallInfoIdx,
3229 func: StackIdx,
3230 narg1: i32,
3231 delta: i32,
3232 ) -> Result<i32, LuaError> {
3233 crate::do_::pretailcall(self, ci, func, narg1, delta)
3234 }
3235 #[inline(always)]
3236 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
3237 where
3238 <N as TryInto<i32>>::Error: std::fmt::Debug,
3239 {
3240 let n = nres.try_into().expect("poscall: nres out of i32 range");
3241 crate::do_::poscall(self, ci, n)
3242 }
3243 pub fn adjust_results(&mut self, nresults: i32) {
3244 const LUA_MULTRET: i32 = -1;
3245 if nresults <= LUA_MULTRET {
3246 let ci_idx = self.ci.as_usize();
3247 if self.call_info[ci_idx].top.0 < self.top.0 {
3248 self.call_info[ci_idx].top = self.top;
3249 }
3250 }
3251 }
3252 pub fn adjust_varargs(
3253 &mut self,
3254 ci: CallInfoIdx,
3255 nfixparams: i32,
3256 cl: &GcRef<lua_types::closure::LuaLClosure>,
3257 ) -> Result<(), LuaError> {
3258 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
3259 }
3260 pub fn get_varargs(&mut self, ci: CallInfoIdx, ra: StackIdx, n: i32) -> Result<i32, LuaError> {
3261 crate::tagmethods::get_varargs(self, ci, ra, n)?;
3262 Ok(0)
3263 }
3264
3265 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
3266 crate::func::close_upval(self, level);
3267 Ok(())
3268 }
3269 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
3270 crate::func::close_upval(self, level);
3271 Ok(())
3272 }
3273 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
3274 let base = self.ci_base(ci);
3275 crate::func::close_upval(self, base);
3276 Ok(())
3277 }
3278
3279 pub fn arith_op(
3280 &mut self,
3281 op: i32,
3282 p1: &LuaValue,
3283 p2: &LuaValue,
3284 ) -> Result<LuaValue, LuaError> {
3285 let arith_op = match op {
3286 0 => lua_types::arith::ArithOp::Add,
3287 1 => lua_types::arith::ArithOp::Sub,
3288 2 => lua_types::arith::ArithOp::Mul,
3289 3 => lua_types::arith::ArithOp::Mod,
3290 4 => lua_types::arith::ArithOp::Pow,
3291 5 => lua_types::arith::ArithOp::Div,
3292 6 => lua_types::arith::ArithOp::Idiv,
3293 7 => lua_types::arith::ArithOp::Band,
3294 8 => lua_types::arith::ArithOp::Bor,
3295 9 => lua_types::arith::ArithOp::Bxor,
3296 10 => lua_types::arith::ArithOp::Shl,
3297 11 => lua_types::arith::ArithOp::Shr,
3298 12 => lua_types::arith::ArithOp::Unm,
3299 13 => lua_types::arith::ArithOp::Bnot,
3300 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
3301 };
3302 let mut res = LuaValue::Nil;
3303 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
3304 Ok(res)
3305 } else {
3306 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
3307 }
3308 }
3309 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
3310 crate::vm::concat(self, n)
3311 }
3312 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3313 crate::vm::less_than(self, l, r)
3314 }
3315 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3316 crate::vm::less_equal(self, l, r)
3317 }
3318 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
3319 crate::vm::equal_obj(None, l, r).unwrap_or(false)
3320 }
3321 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3322 crate::vm::equal_obj(Some(self), l, r)
3323 }
3324 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
3325 match v {
3326 LuaValue::Table(_) => {
3327 let consult_len_tm =
3330 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
3331 let tm = if consult_len_tm {
3332 let mt = self.table_metatable(v);
3333 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
3334 } else {
3335 LuaValue::Nil
3336 };
3337 if matches!(tm, LuaValue::Nil) {
3338 let n = self.table_length(v)?;
3339 return Ok(LuaValue::Int(n));
3340 }
3341 self.push(LuaValue::Nil);
3342 let slot = StackIdx(self.top.0 - 1);
3343 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3344 Ok(self.pop())
3345 }
3346 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
3347 other => {
3348 let tm = crate::tagmethods::get_tm_by_obj(
3349 self,
3350 other,
3351 crate::tagmethods::TagMethod::Len,
3352 );
3353 if matches!(tm, LuaValue::Nil) {
3354 let mut msg = b"attempt to get length of a ".to_vec();
3355 msg.extend_from_slice(&self.obj_type_name(other));
3356 msg.extend_from_slice(b" value");
3357 return Err(crate::debug::prefixed_runtime_pub(self, msg));
3358 }
3359 self.push(LuaValue::Nil);
3360 let slot = StackIdx(self.top.0 - 1);
3361 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3362 Ok(self.pop())
3363 }
3364 }
3365 }
3366 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
3367 let slot: StackIdx = if idx > 0 {
3368 let ci_func = self.current_call_info().func;
3369 ci_func + idx
3370 } else {
3371 debug_assert!(idx != 0, "invalid index");
3372 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3373 };
3374 let val = self.get_at(slot);
3375 match val {
3376 LuaValue::Str(s) => Ok(s),
3377 LuaValue::Int(_) | LuaValue::Float(_) => {
3378 let s = crate::object::num_to_string(self, &val)?;
3379 self.set_at(slot, LuaValue::Str(s.clone()));
3380 Ok(s)
3381 }
3382 _ => Err(LuaError::type_error(&val, "convert to string")),
3383 }
3384 }
3385 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3386 let val = self.get_at(idx);
3387 match val {
3388 LuaValue::Str(s) => Ok(s),
3389 LuaValue::Int(_) | LuaValue::Float(_) => {
3390 let s = crate::object::num_to_string(self, &val)?;
3391 self.set_at(idx, LuaValue::Str(s.clone()));
3392 Ok(s)
3393 }
3394 _ => Err(LuaError::type_error(&val, "convert to string")),
3395 }
3396 }
3397 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3398 let mut out = LuaValue::Nil;
3399 let sz = crate::object::str2num(s, &mut out);
3400 if sz == 0 {
3401 None
3402 } else {
3403 Some((out, sz))
3404 }
3405 }
3406
3407 #[inline(always)]
3408 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3409 let LuaValue::Table(tbl) = t else {
3410 return Ok(None);
3411 };
3412 let v = tbl.get(k);
3413 if matches!(v, LuaValue::Nil) {
3414 Ok(None)
3415 } else {
3416 Ok(Some(v))
3417 }
3418 }
3419 #[inline(always)]
3420 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3421 let LuaValue::Table(tbl) = t else {
3422 return Ok(None);
3423 };
3424 let v = tbl.get_int(k);
3425 if matches!(v, LuaValue::Nil) {
3426 Ok(None)
3427 } else {
3428 Ok(Some(v))
3429 }
3430 }
3431 #[inline(always)]
3432 pub fn fast_get_short_str(
3433 &mut self,
3434 t: &LuaValue,
3435 k: &LuaValue,
3436 ) -> Result<Option<LuaValue>, LuaError> {
3437 let LuaValue::Table(tbl) = t else {
3438 return Ok(None);
3439 };
3440 let LuaValue::Str(s) = k else {
3441 return Ok(None);
3442 };
3443 let v = tbl.get_short_str(s);
3444 if matches!(v, LuaValue::Nil) {
3445 Ok(None)
3446 } else {
3447 Ok(Some(v))
3448 }
3449 }
3450 #[inline(always)]
3451 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3452 let Some(mt) = t else {
3453 return LuaValue::Nil;
3454 };
3455 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3456 let ename = self.global().tmname[tm as usize].clone();
3457 mt.get_short_str(&ename)
3458 }
3459 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3460 let mt = u.metatable();
3462 self.fast_tm_table(mt.as_ref(), tm)
3463 }
3464
3465 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3466 if let LuaValue::Table(tbl) = t {
3472 if tbl.metatable().is_none() {
3473 return Ok(tbl.get(k));
3474 }
3475 }
3476 if let Some(v) = self.fast_get(t, k)? {
3477 return Ok(v);
3478 }
3479 let res = self.top_idx();
3480 self.push(LuaValue::Nil);
3481 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None, None)?;
3482 let value = self.get_at(res);
3483 self.pop();
3484 Ok(value)
3485 }
3486 #[inline]
3501 pub fn table_set_with_tm(
3502 &mut self,
3503 t: &LuaValue,
3504 k: LuaValue,
3505 v: LuaValue,
3506 ) -> Result<(), LuaError> {
3507 if let LuaValue::Table(tbl) = t {
3508 if tbl.metatable().is_none() {
3509 self.gc_table_barrier_back(tbl, &v);
3510 return self.table_raw_set(t, k, v);
3511 }
3512 }
3513 if self.fast_get(t, &k)?.is_some() {
3514 self.gc_value_barrier_back(t, &v);
3515 return self.table_raw_set(t, k, v);
3516 }
3517 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3518 }
3519 #[inline]
3520 pub fn table_raw_set(
3521 &mut self,
3522 t: &LuaValue,
3523 k: LuaValue,
3524 v: LuaValue,
3525 ) -> Result<(), LuaError> {
3526 let LuaValue::Table(tbl) = t else {
3527 return Err(LuaError::type_error(t, "index"));
3528 };
3529 let tbl = tbl.clone();
3530 tbl.raw_set(self, k, v)
3531 }
3532 #[inline]
3533 pub fn table_array_set(
3534 &mut self,
3535 t: &LuaValue,
3536 idx: usize,
3537 v: LuaValue,
3538 ) -> Result<(), LuaError> {
3539 let LuaValue::Table(tbl) = t else {
3540 return Err(LuaError::type_error(t, "index"));
3541 };
3542 let tbl = tbl.clone();
3543 tbl.raw_set_int(self, idx as i64 + 1, v)
3544 }
3545 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3546 let LuaValue::Table(tbl) = t else {
3547 return Err(LuaError::type_error(t, "index"));
3548 };
3549 if n > tbl.array_len() {
3550 tbl.resize(self, n, 0)?;
3551 }
3552 Ok(())
3553 }
3554 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3555 let LuaValue::Table(tbl) = t else {
3556 return Err(LuaError::type_error(t, "get length of"));
3557 };
3558 Ok(tbl.getn() as i64)
3559 }
3560 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3561 match v {
3562 LuaValue::Table(t) => t.metatable(),
3563 LuaValue::UserData(u) => u.metatable(),
3564 other => {
3565 let idx = other.base_type() as usize;
3566 self.global().mt[idx].clone()
3567 }
3568 }
3569 }
3570 pub fn table_resize(
3571 &mut self,
3572 t: &GcRef<LuaTable>,
3573 na: usize,
3574 nh: usize,
3575 ) -> Result<(), LuaError> {
3576 self.mark_gc_check_needed();
3577 t.resize(self, na, nh)
3578 }
3579 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3580 let mut i: i64 = 1;
3588 loop {
3589 let v = t.get_int(i);
3590 if matches!(v, LuaValue::Nil) {
3591 return i - 1;
3592 }
3593 i += 1;
3594 }
3595 }
3596
3597 pub fn try_bin_tm(
3598 &mut self,
3599 p1: &LuaValue,
3600 p1_idx: Option<StackIdx>,
3601 p2: &LuaValue,
3602 p2_idx: Option<StackIdx>,
3603 res: StackIdx,
3604 tm: lua_types::tagmethod::TagMethod,
3605 ) -> Result<(), LuaError> {
3606 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3607 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3608 }
3609 pub fn try_bin_i_tm(
3610 &mut self,
3611 p1: &LuaValue,
3612 p1_idx: Option<StackIdx>,
3613 imm: i64,
3614 flip: bool,
3615 res: StackIdx,
3616 tm: lua_types::tagmethod::TagMethod,
3617 ) -> Result<(), LuaError> {
3618 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3619 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3620 }
3621 pub fn try_bin_assoc_tm(
3622 &mut self,
3623 p1: &LuaValue,
3624 p1_idx: Option<StackIdx>,
3625 p2: &LuaValue,
3626 p2_idx: Option<StackIdx>,
3627 flip: bool,
3628 res: StackIdx,
3629 tm: lua_types::tagmethod::TagMethod,
3630 ) -> Result<(), LuaError> {
3631 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3632 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3633 }
3634 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3635 crate::tagmethods::try_concat_tm(self)
3636 }
3637 pub fn call_tm(
3638 &mut self,
3639 f: LuaValue,
3640 p1: &LuaValue,
3641 p2: &LuaValue,
3642 p3: &LuaValue,
3643 ) -> Result<(), LuaError> {
3644 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3645 }
3646 pub fn call_tm_res(
3647 &mut self,
3648 f: LuaValue,
3649 p1: &LuaValue,
3650 p2: &LuaValue,
3651 res: StackIdx,
3652 ) -> Result<(), LuaError> {
3653 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3654 }
3655 pub fn call_tm_res_bool(
3656 &mut self,
3657 f: LuaValue,
3658 p1: &LuaValue,
3659 p2: &LuaValue,
3660 ) -> Result<bool, LuaError> {
3661 let res = self.top_idx();
3662 self.push(LuaValue::Nil);
3663 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3664 let result = self.get_at(res).clone();
3665 self.pop();
3666 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3667 }
3668 pub fn call_order_tm(
3669 &mut self,
3670 p1: &LuaValue,
3671 p2: &LuaValue,
3672 tm: lua_types::tagmethod::TagMethod,
3673 ) -> Result<bool, LuaError> {
3674 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3675 crate::tagmethods::call_order_tm(self, p1, p2, event)
3676 }
3677 pub fn call_order_i_tm(
3678 &mut self,
3679 p1: &LuaValue,
3680 v2: i64,
3681 flip: bool,
3682 isfloat: bool,
3683 tm: lua_types::tagmethod::TagMethod,
3684 ) -> Result<bool, LuaError> {
3685 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3686 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3687 }
3688
3689 #[inline(always)]
3690 pub fn proto_code(
3691 &self,
3692 cl: &GcRef<lua_types::closure::LuaLClosure>,
3693 pc: u32,
3694 ) -> lua_types::opcode::Instruction {
3695 cl.proto.code[pc as usize]
3696 }
3697 #[inline(always)]
3698 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3699 cl.proto.k[idx].clone()
3700 }
3701 #[inline(always)]
3707 pub fn proto_const_int(
3708 &self,
3709 cl: &GcRef<lua_types::closure::LuaLClosure>,
3710 idx: usize,
3711 ) -> Option<i64> {
3712 match &cl.proto.k[idx] {
3713 LuaValue::Int(v) => Some(*v),
3714 _ => None,
3715 }
3716 }
3717 #[inline(always)]
3721 pub fn proto_const_num(
3722 &self,
3723 cl: &GcRef<lua_types::closure::LuaLClosure>,
3724 idx: usize,
3725 ) -> Option<f64> {
3726 match &cl.proto.k[idx] {
3727 LuaValue::Float(f) => Some(*f),
3728 LuaValue::Int(v) => Some(*v as f64),
3729 _ => None,
3730 }
3731 }
3732 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3733 let cl = self
3734 .ci_lua_closure(ci)
3735 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3736 cl.proto.code[pc as usize]
3737 }
3738 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3743 Ok(crate::debug::trace_call(self)? != 0)
3744 }
3745 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3748 Ok(crate::debug::trace_exec(self, pc)? != 0)
3749 }
3750 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3751 crate::do_::hookcall(self, idx)
3752 }
3753 #[inline(always)]
3754 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3755 let g = self.global();
3756 if !g.is_gc_running() {
3757 return None;
3758 }
3759 let should_collect = g.heap.would_collect();
3760 let has_finalizers = g.finalizers.has_to_be_finalized();
3761 if should_collect || has_finalizers {
3762 Some((should_collect, has_finalizers))
3763 } else {
3764 None
3765 }
3766 }
3767
3768 #[inline(always)]
3769 fn should_check_gc(&mut self) -> bool {
3770 if self.gc_check_needed {
3771 return true;
3772 }
3773 if self.global().finalizers.has_to_be_finalized() {
3774 self.gc_check_needed = true;
3775 return true;
3776 }
3777 false
3778 }
3779
3780 #[inline(always)]
3781 pub(crate) fn mark_gc_check_needed(&mut self) {
3782 self.gc_check_needed = true;
3783 }
3784
3785 #[inline(always)]
3786 pub fn gc_check_step(&mut self) {
3787 if !self.allowhook {
3788 return;
3789 }
3790 if !self.should_check_gc() {
3791 return;
3792 }
3793 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3794 self.gc_check_needed = false;
3795 return;
3796 };
3797 if should_collect || has_finalizers {
3798 if should_collect {
3799 self.gc().check_step();
3800 }
3801 crate::api::run_pending_finalizers(self);
3802 self.gc_check_needed = true;
3803 }
3804 let should_keep_checking = {
3805 let g = self.global();
3806 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3807 };
3808 self.gc_check_needed = should_keep_checking;
3809 }
3810 #[inline(always)]
3811 pub fn gc_cond_step(&mut self) {
3812 if !self.allowhook {
3813 return;
3814 }
3815 if !self.should_check_gc() {
3816 return;
3817 }
3818 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3819 self.gc_check_needed = false;
3820 return;
3821 };
3822 if should_collect || has_finalizers {
3823 if should_collect {
3824 self.gc().check_step();
3825 }
3826 crate::api::run_pending_finalizers(self);
3827 self.gc_check_needed = true;
3828 }
3829 let should_keep_checking = {
3830 let g = self.global();
3831 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3832 };
3833 self.gc_check_needed = should_keep_checking;
3834 }
3835 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3836 self.gc().barrier_back(t, v);
3837 }
3838 pub fn gc_value_barrier_back(&mut self, t: &LuaValue, v: &LuaValue) {
3839 if let LuaValue::Table(tbl) = t {
3840 self.gc_table_barrier_back(tbl, v);
3841 } else {
3842 self.gc_barrier_back(t, v);
3843 }
3844 }
3845 pub fn gc_table_barrier_back(&mut self, t: &GcRef<LuaTable>, v: &LuaValue) {
3846 self.gc().table_barrier_back(t, v);
3847 }
3848 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3849 self.gc().barrier(uv, v);
3850 }
3851 pub fn is_main_thread(&mut self) -> bool {
3857 let g = self.global();
3858 g.current_thread_id == g.main_thread_id
3859 }
3860 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3861 match v {
3862 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3863 LuaValue::Table(t) => {
3864 if let Some(mt) = t.metatable() {
3865 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3866 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3867 }
3868 }
3869 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3870 }
3871 LuaValue::UserData(u) => {
3872 if let Some(mt) = u.metatable() {
3873 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3874 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3875 }
3876 }
3877 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3878 }
3879 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3880 }
3881 }
3882
3883 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3884 crate::tagmethods::obj_type_name(self, v)
3885 }
3886 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) {
3887 warning(self, _msg, _to_cont)
3888 }
3889}
3890
3891pub struct GcHandle<'a> {
3897 _state: &'a mut LuaState,
3898}
3899
3900struct CollectRoots<'a> {
3907 global: &'a GlobalState,
3908 thread: &'a LuaState,
3909}
3910
3911#[derive(Clone, Copy)]
3912enum HeapCollectMode {
3913 Full,
3914 Step,
3915 Minor,
3916}
3917
3918impl<'a> lua_gc::Trace for CollectRoots<'a> {
3919 fn trace(&self, m: &mut lua_gc::Marker) {
3920 self.global.trace(m);
3921 self.thread.trace(m);
3922 }
3923}
3924
3925#[derive(Clone, Copy)]
3926enum BarrierKind {
3927 Forward,
3928 Backward,
3929}
3930
3931fn barrier_lua_value<P>(
3932 heap: &lua_gc::Heap,
3933 parent: GcRef<P>,
3934 child: &LuaValue,
3935 generational: bool,
3936 kind: BarrierKind,
3937) where
3938 P: lua_gc::Trace + 'static,
3939{
3940 if generational && matches!(kind, BarrierKind::Backward) {
3941 heap.generational_backward_barrier(parent.0);
3942 }
3943 match child {
3944 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3945 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3946 LuaValue::Function(LuaClosure::Lua(c)) => {
3947 barrier_gc_child(heap, parent, *c, generational, kind)
3948 }
3949 LuaValue::Function(LuaClosure::C(c)) => {
3950 barrier_gc_child(heap, parent, *c, generational, kind)
3951 }
3952 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3953 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3954 LuaValue::Nil
3955 | LuaValue::Bool(_)
3956 | LuaValue::Int(_)
3957 | LuaValue::Float(_)
3958 | LuaValue::LightUserData(_)
3959 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3960 }
3961}
3962
3963fn barrier_gc_child<P, C>(
3964 heap: &lua_gc::Heap,
3965 parent: GcRef<P>,
3966 child: GcRef<C>,
3967 generational: bool,
3968 kind: BarrierKind,
3969) where
3970 P: lua_gc::Trace + 'static,
3971 C: lua_gc::Trace + 'static,
3972{
3973 if generational && matches!(kind, BarrierKind::Forward) {
3974 heap.generational_forward_barrier(parent.0, child.0);
3975 } else if matches!(kind, BarrierKind::Backward) {
3976 heap.barrier_back(parent.0, child.0);
3977 } else {
3978 heap.barrier(parent.0, child.0);
3979 }
3980}
3981
3982fn barrier_child_any<P>(
3983 heap: &lua_gc::Heap,
3984 parent: GcRef<P>,
3985 child: &dyn std::any::Any,
3986 generational: bool,
3987 kind: BarrierKind,
3988) where
3989 P: lua_gc::Trace + 'static,
3990{
3991 if let Some(v) = child.downcast_ref::<LuaValue>() {
3992 barrier_lua_value(heap, parent, v, generational, kind);
3993 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
3994 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3995 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
3996 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3997 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
3998 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3999 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
4000 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4001 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
4002 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4003 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
4004 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4005 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
4006 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4007 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
4008 barrier_gc_child(heap, parent, c.clone(), generational, kind);
4009 }
4010}
4011
4012fn barrier_any(
4013 heap: &lua_gc::Heap,
4014 parent: &dyn std::any::Any,
4015 child: &dyn std::any::Any,
4016 generational: bool,
4017 kind: BarrierKind,
4018) {
4019 if let Some(v) = parent.downcast_ref::<LuaValue>() {
4020 match v {
4021 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
4022 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
4023 LuaValue::Function(LuaClosure::Lua(p)) => {
4024 barrier_child_any(heap, *p, child, generational, kind)
4025 }
4026 LuaValue::Function(LuaClosure::C(p)) => {
4027 barrier_child_any(heap, *p, child, generational, kind)
4028 }
4029 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
4030 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
4031 LuaValue::Nil
4032 | LuaValue::Bool(_)
4033 | LuaValue::Int(_)
4034 | LuaValue::Float(_)
4035 | LuaValue::LightUserData(_)
4036 | LuaValue::Function(LuaClosure::LightC(_)) => {}
4037 }
4038 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
4039 barrier_child_any(heap, p.clone(), child, generational, kind);
4040 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
4041 barrier_child_any(heap, p.clone(), child, generational, kind);
4042 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
4043 barrier_child_any(heap, p.clone(), child, generational, kind);
4044 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
4045 barrier_child_any(heap, p.clone(), child, generational, kind);
4046 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
4047 barrier_child_any(heap, p.clone(), child, generational, kind);
4048 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
4049 barrier_child_any(heap, p.clone(), child, generational, kind);
4050 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
4051 barrier_child_any(heap, p.clone(), child, generational, kind);
4052 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
4053 barrier_child_any(heap, p.clone(), child, generational, kind);
4054 }
4055}
4056
4057fn trace_reachable_threads(
4058 global: &GlobalState,
4059 _current_thread_id: u64,
4060 marker: &mut lua_gc::Marker,
4061) {
4062 use lua_gc::Trace;
4063
4064 loop {
4065 let visited_before = marker.visited_count();
4066 for (id, entry) in global.threads.iter() {
4067 if thread_entry_marked_alive(marker, *id, entry) {
4068 if let Ok(thread) = entry.state.try_borrow() {
4069 thread.trace(marker);
4070 }
4071 }
4072 }
4073 marker.drain_gray_queue();
4074 if marker.visited_count() == visited_before {
4075 break;
4076 }
4077 }
4078}
4079
4080fn thread_entry_marked_alive(
4081 marker: &lua_gc::Marker,
4082 id: u64,
4083 entry: &ThreadRegistryEntry,
4084) -> bool {
4085 marker.is_marked_or_old(entry.value.0) && entry.value.id == id
4086}
4087
4088fn lua_value_marked_or_old(marker: &lua_gc::Marker, value: &LuaValue) -> bool {
4089 match value {
4090 LuaValue::Str(v) => marker.is_marked_or_old(v.0),
4091 LuaValue::Table(v) => marker.is_marked_or_old(v.0),
4092 LuaValue::Function(LuaClosure::Lua(v)) => marker.is_marked_or_old(v.0),
4093 LuaValue::Function(LuaClosure::C(v)) => marker.is_marked_or_old(v.0),
4094 LuaValue::UserData(v) => marker.is_marked_or_old(v.0),
4095 LuaValue::Thread(v) => marker.is_marked_or_old(v.0),
4096 LuaValue::Nil
4097 | LuaValue::Bool(_)
4098 | LuaValue::Int(_)
4099 | LuaValue::Float(_)
4100 | LuaValue::LightUserData(_)
4101 | LuaValue::Function(LuaClosure::LightC(_)) => true,
4102 }
4103}
4104
4105fn lua_value_identity(value: &LuaValue) -> Option<usize> {
4106 match value {
4107 LuaValue::Str(v) => Some(v.identity()),
4108 LuaValue::Table(v) => Some(v.identity()),
4109 LuaValue::Function(LuaClosure::Lua(v)) => Some(v.identity()),
4110 LuaValue::Function(LuaClosure::C(v)) => Some(v.identity()),
4111 LuaValue::UserData(v) => Some(v.identity()),
4112 LuaValue::Thread(v) => Some(v.identity()),
4113 LuaValue::Nil
4114 | LuaValue::Bool(_)
4115 | LuaValue::Int(_)
4116 | LuaValue::Float(_)
4117 | LuaValue::LightUserData(_)
4118 | LuaValue::Function(LuaClosure::LightC(_)) => None,
4119 }
4120}
4121
4122fn finalizer_marked_or_old(marker: &lua_gc::Marker, object: &FinalizerObject) -> bool {
4123 match object {
4124 FinalizerObject::Table(t) => marker.is_marked_or_old(t.0),
4125 FinalizerObject::UserData(u) => marker.is_marked_or_old(u.0),
4126 }
4127}
4128
4129fn weak_snapshot_tables<'a>(
4130 snapshot: &'a lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>>,
4131) -> impl Iterator<Item = &'a GcRef<LuaTable>> {
4132 snapshot
4133 .weak_values
4134 .iter()
4135 .chain(snapshot.ephemeron.iter())
4136 .chain(snapshot.all_weak.iter())
4137}
4138
4139fn close_open_upvalues_for_unreachable_threads(global: &GlobalState, marker: &mut lua_gc::Marker) {
4140 use lua_gc::Trace;
4141
4142 let mut closed_values = Vec::<LuaValue>::new();
4143 for (id, entry) in global.threads.iter() {
4144 if entry.value.id != *id {
4145 continue;
4146 }
4147 if thread_entry_marked_alive(marker, *id, entry) {
4148 continue;
4149 }
4150 let Ok(thread) = entry.state.try_borrow() else {
4151 continue;
4152 };
4153 for uv in thread.openupval.iter() {
4154 if !marker.is_visited(uv.identity()) {
4155 continue;
4156 }
4157 let Some((thread_id, idx)) = uv.try_open_payload() else {
4158 continue;
4159 };
4160 if thread_id as u64 != *id {
4161 continue;
4162 }
4163 let value = thread.get_at(idx);
4164 uv.close_with(value.clone());
4165 closed_values.push(value);
4166 }
4167 }
4168 for value in closed_values {
4169 value.trace(marker);
4170 }
4171 marker.drain_gray_queue();
4172}
4173
4174fn record_live_interned_strings(
4175 global: &GlobalState,
4176 marker: &lua_gc::Marker,
4177 live_ids: &std::cell::RefCell<Vec<usize>>,
4178) {
4179 let mut live = live_ids.borrow_mut();
4180 for s in global.interned_lt.values() {
4181 let id = s.identity();
4182 if marker.is_visited(id) {
4183 live.push(id);
4184 }
4185 }
4186}
4187
4188fn retain_live_interned_strings(global: &mut GlobalState, mut live_ids: Vec<usize>) {
4189 if live_ids.is_empty() {
4190 global.interned_lt.clear();
4191 return;
4192 }
4193 if live_ids.len() == global.interned_lt.len() {
4194 return;
4195 }
4196 live_ids.sort_unstable();
4197 live_ids.dedup();
4198 global
4199 .interned_lt
4200 .retain(|_, s| live_ids.binary_search(&s.identity()).is_ok());
4201}
4202
4203impl<'a> GcHandle<'a> {
4204 pub fn check_step(&self) {
4211 if !self._state.global().is_gc_running() {
4212 return;
4213 }
4214 if self._state.global().is_gen_mode() {
4215 let should_collect = {
4216 let g = self._state.global();
4217 g.heap.would_collect() || g.gc_debt() > 0
4218 };
4219 if should_collect {
4220 self.generational_step();
4221 }
4222 } else {
4223 self.collect_via_heap(false);
4224 }
4225 }
4226
4227 pub fn full_collect(&self) {
4229 if self._state.global().is_gen_mode() {
4230 self.fullgen();
4231 } else {
4232 self.collect_via_heap(true);
4233 }
4234 }
4235
4236 fn negative_debt(bytes: usize) -> isize {
4237 -(bytes.min(isize::MAX as usize) as isize)
4238 }
4239
4240 fn set_minor_debt(&self) {
4241 let mut g = self._state.global_mut();
4242 let total = g.total_bytes();
4243 let growth = (total / 100).saturating_mul(g.genminormul as usize);
4244 g.heap
4245 .set_threshold_bytes(total.saturating_add(growth.max(1)));
4246 set_debt(&mut *g, Self::negative_debt(growth));
4247 }
4248
4249 fn set_pause_debt(&self) {
4250 let mut g = self._state.global_mut();
4251 let total = g.total_bytes();
4252 let pause = g.gc_pause_param().max(0) as usize;
4253 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
4254 let debt = if threshold > total {
4255 Self::negative_debt(threshold - total)
4256 } else {
4257 0
4258 };
4259 let heap_threshold = if threshold > total {
4260 threshold
4261 } else {
4262 total.saturating_add(1)
4263 };
4264 g.heap.set_threshold_bytes(heap_threshold);
4265 set_debt(&mut *g, debt);
4266 }
4267
4268 fn enter_incremental_mode(&self) {
4269 let mut g = self._state.global_mut();
4270 g.heap.reset_all_ages();
4271 g.finalizers.reset_generation_boundaries();
4272 g.gckind = GcKind::Incremental as u8;
4273 g.lastatomic = 0;
4274 }
4275
4276 fn enter_generational_mode(&self) -> usize {
4277 self.collect_via_heap_mode(HeapCollectMode::Full);
4278 let numobjs = {
4279 let mut g = self._state.global_mut();
4280 g.heap.promote_all_to_old();
4281 g.finalizers.promote_all_pending_to_old();
4282 g.heap.allgc_count()
4283 };
4284 let total = self._state.global().total_bytes();
4285 {
4286 let mut g = self._state.global_mut();
4287 g.gckind = GcKind::Generational as u8;
4288 g.lastatomic = 0;
4289 g.gc_estimate = total;
4290 }
4291 self.set_minor_debt();
4292 numobjs
4293 }
4294
4295 fn fullgen(&self) -> usize {
4296 self.enter_incremental_mode();
4297 self.enter_generational_mode()
4298 }
4299
4300 fn stepgenfull(&self, lastatomic: usize) {
4301 if self._state.global().gckind == GcKind::Generational as u8 {
4302 self.enter_incremental_mode();
4303 }
4304 self.collect_via_heap_mode(HeapCollectMode::Full);
4305 let newatomic = self._state.global().heap.allgc_count().max(1);
4306 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
4307 {
4308 let mut g = self._state.global_mut();
4309 g.heap.promote_all_to_old();
4310 g.finalizers.promote_all_pending_to_old();
4311 }
4312 let total = self._state.global().total_bytes();
4313 {
4314 let mut g = self._state.global_mut();
4315 g.gckind = GcKind::Generational as u8;
4316 g.lastatomic = 0;
4317 g.gc_estimate = total;
4318 }
4319 self.set_minor_debt();
4320 } else {
4321 {
4322 let mut g = self._state.global_mut();
4323 g.heap.reset_all_ages();
4324 g.finalizers.reset_generation_boundaries();
4325 }
4326 let total = self._state.global().total_bytes();
4327 {
4328 let mut g = self._state.global_mut();
4329 g.gckind = GcKind::Incremental as u8;
4330 g.lastatomic = newatomic;
4331 g.gc_estimate = total;
4332 }
4333 self.set_pause_debt();
4334 }
4335 }
4336
4337 fn collect_via_heap(&self, force: bool) {
4346 self.collect_via_heap_mode(if force {
4347 HeapCollectMode::Full
4348 } else {
4349 HeapCollectMode::Step
4350 });
4351 }
4352
4353 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
4354 use lua_gc::Trace;
4355 let state_ref: &LuaState = &*self._state;
4356
4357 if matches!(mode, HeapCollectMode::Step) {
4363 let g = state_ref.global.borrow();
4364 if !g.heap.would_collect() {
4365 return;
4366 }
4367 }
4368
4369 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4373 let mut g = state_ref.global.borrow_mut();
4374 g.weak_tables_registry.live_snapshot_by_kind()
4375 };
4376
4377 let weak_table_capacity = weak_tables_snapshot.len();
4382 let (pending_snapshot, thread_capacity, interned_capacity): (
4383 Vec<FinalizerObject>,
4384 usize,
4385 usize,
4386 ) = {
4387 let g = state_ref.global.borrow();
4388 let pending = match mode {
4389 HeapCollectMode::Minor => g.finalizers.pending_minor_snapshot(),
4390 HeapCollectMode::Full | HeapCollectMode::Step => g.finalizers.pending_snapshot(),
4391 };
4392 (pending, g.threads.len(), g.interned_lt.len())
4393 };
4394 let finalizer_capacity = pending_snapshot.len();
4395
4396 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4397 std::cell::RefCell::new(std::collections::HashSet::new());
4398 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4399 std::cell::RefCell::new(Vec::new());
4400 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4401 std::cell::RefCell::new(std::collections::HashSet::new());
4402 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4403 std::cell::RefCell::new(std::collections::HashSet::new());
4404 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4405 let collect_ran = std::cell::Cell::new(false);
4406
4407 {
4408 let global = state_ref.global.borrow();
4409 global.heap.unpause();
4410 let roots = CollectRoots {
4411 global: &*global,
4412 thread: state_ref,
4413 };
4414 let hook = |marker: &mut lua_gc::Marker| {
4415 collect_ran.set(true);
4416 alive_ids.borrow_mut().reserve(weak_table_capacity);
4417 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4418 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4419 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4420 live_interned_ids.borrow_mut().reserve(interned_capacity);
4421 trace_reachable_threads(&*global, global.current_thread_id, marker);
4422 close_open_upvalues_for_unreachable_threads(&*global, marker);
4423 loop {
4424 let visited_before = marker.visited_count();
4425 for t in &weak_tables_snapshot.ephemeron {
4426 if !marker.is_marked_or_old(t.0) {
4427 continue;
4428 }
4429 let to_mark = t.ephemeron_values_to_mark_with_value(&|v| {
4430 lua_value_marked_or_old(marker, v)
4431 });
4432 for v in &to_mark {
4433 v.trace(marker);
4434 }
4435 }
4436 marker.drain_gray_queue();
4437 if marker.visited_count() == visited_before {
4438 break;
4439 }
4440 }
4441 for pf in &pending_snapshot {
4442 if !finalizer_marked_or_old(marker, pf) {
4443 pf.mark(marker);
4444 finalizing_ids.borrow_mut().insert(pf.identity());
4445 newly_unreachable.borrow_mut().push(pf.clone());
4446 }
4447 }
4448 marker.drain_gray_queue();
4449 loop {
4450 let visited_before = marker.visited_count();
4451 for t in &weak_tables_snapshot.ephemeron {
4452 if !marker.is_marked_or_old(t.0) {
4453 continue;
4454 }
4455 let to_mark = t.ephemeron_values_to_mark_with_value(&|v| {
4456 lua_value_marked_or_old(marker, v)
4457 });
4458 for v in &to_mark {
4459 v.trace(marker);
4460 }
4461 }
4462 marker.drain_gray_queue();
4463 if marker.visited_count() == visited_before {
4464 break;
4465 }
4466 }
4467 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4468 let id = t.identity();
4469 if marker.is_marked_or_old(t.0) {
4470 let to_mark = {
4471 let finalizing = finalizing_ids.borrow();
4472 t.prune_weak_dead_with_value(
4473 &|v| lua_value_marked_or_old(marker, v),
4474 &|v| {
4475 lua_value_marked_or_old(marker, v)
4476 && lua_value_identity(v)
4477 .map_or(true, |id| !finalizing.contains(&id))
4478 },
4479 )
4480 };
4481 for v in &to_mark {
4482 v.trace(marker);
4483 }
4484 alive_ids.borrow_mut().insert(id);
4485 }
4486 }
4487 marker.drain_gray_queue();
4488 {
4489 let mut alive = alive_thread_ids.borrow_mut();
4490 for (id, entry) in global.threads.iter() {
4491 if thread_entry_marked_alive(marker, *id, entry) {
4492 alive.insert(*id);
4493 }
4494 }
4495 }
4496 record_live_interned_strings(&*global, marker, &live_interned_ids);
4497 };
4498 match mode {
4499 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
4500 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
4501 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
4502 }
4503 }
4504
4505 if !collect_ran.get() {
4506 return;
4507 }
4508
4509 let alive_set = alive_ids.into_inner();
4513 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4514 let alive_thread_ids = alive_thread_ids.into_inner();
4515 let live_interned_ids = live_interned_ids.into_inner();
4516 let mut g = state_ref.global.borrow_mut();
4517 retain_live_interned_strings(&mut *g, live_interned_ids);
4518 g.weak_tables_registry.retain_identities(&alive_set);
4519 let main_thread_id = g.main_thread_id;
4520 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4521 g.cross_thread_upvals
4522 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4523 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4527 for object in &promoted {
4528 if let Some(ptr) = object.heap_ptr() {
4529 g.heap.move_finobj_to_tobefnz(ptr);
4530 }
4531 }
4532 if matches!(mode, HeapCollectMode::Minor) {
4533 g.finalizers.finish_minor_collection();
4534 }
4535 }
4536
4537 pub fn generational_step(&self) -> bool {
4539 self.generational_step_with_major(true)
4540 }
4541
4542 pub fn generational_step_minor_only(&self) -> bool {
4548 self.generational_step_with_major(false)
4549 }
4550
4551 fn generational_step_with_major(&self, allow_major: bool) -> bool {
4552 let (lastatomic, majorbase, majorinc, should_major) = {
4553 let g = self._state.global();
4554 let majorbase = if g.gc_estimate == 0 {
4555 g.total_bytes()
4556 } else {
4557 g.gc_estimate
4558 };
4559 let majormul = g.gc_genmajormul_param().max(0) as usize;
4560 let majorinc = (majorbase / 100).saturating_mul(majormul);
4561 let debt_due = g.gc_debt() > 0 || g.heap.would_collect();
4562 let should_major =
4563 allow_major && debt_due && g.total_bytes() > majorbase.saturating_add(majorinc);
4564 (g.lastatomic, majorbase, majorinc, should_major)
4565 };
4566
4567 if lastatomic != 0 {
4568 self.stepgenfull(lastatomic);
4569 debug_assert!(self._state.global().is_gen_mode());
4570 return true;
4571 }
4572
4573 if should_major {
4574 let numobjs = self.fullgen();
4575 let after = self._state.global().total_bytes();
4576 if after < majorbase.saturating_add(majorinc / 2) {
4577 self.set_minor_debt();
4578 } else {
4579 {
4580 let mut g = self._state.global_mut();
4581 g.lastatomic = numobjs.max(1);
4582 }
4583 self.set_pause_debt();
4584 }
4585 } else {
4586 self.collect_via_heap_mode(HeapCollectMode::Minor);
4587 self.set_minor_debt();
4588 self._state.global_mut().gc_estimate = majorbase;
4589 }
4590
4591 debug_assert!(self._state.global().is_gen_mode());
4592 true
4593 }
4594
4595 pub fn step(&self) { }
4598
4599 pub fn incremental_step(&self, work_units: isize) -> bool {
4612 self.incremental_step_to_state(work_units, None)
4613 }
4614
4615 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4620 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4621 self._state.global().heap.gc_state() == target
4622 }
4623
4624 fn incremental_step_to_state(
4625 &self,
4626 work_units: isize,
4627 target: Option<lua_gc::GcState>,
4628 ) -> bool {
4629 use lua_gc::{StepBudget, StepOutcome, Trace};
4630 let state_ref: &LuaState = &*self._state;
4631
4632 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4633 let mut g = state_ref.global.borrow_mut();
4634 g.weak_tables_registry.live_snapshot_by_kind()
4635 };
4636
4637 let weak_table_capacity = weak_tables_snapshot.len();
4638 let (pending_snapshot, thread_capacity, interned_capacity): (
4639 Vec<FinalizerObject>,
4640 usize,
4641 usize,
4642 ) = {
4643 let g = state_ref.global.borrow();
4644 (
4645 g.finalizers.pending_snapshot(),
4646 g.threads.len(),
4647 g.interned_lt.len(),
4648 )
4649 };
4650 let finalizer_capacity = pending_snapshot.len();
4651
4652 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4653 std::cell::RefCell::new(std::collections::HashSet::new());
4654 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4655 std::cell::RefCell::new(Vec::new());
4656 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4657 std::cell::RefCell::new(std::collections::HashSet::new());
4658 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4659 std::cell::RefCell::new(std::collections::HashSet::new());
4660 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4661 let atomic_ran = std::cell::Cell::new(false);
4662
4663 let stop_target = {
4664 let g = state_ref.global.borrow();
4665 match (target, g.heap.gc_state()) {
4666 (Some(target), _) => Some(target),
4667 (None, lua_gc::GcState::CallFin) => None,
4668 (None, _) => Some(lua_gc::GcState::CallFin),
4669 }
4670 };
4671
4672 let outcome = {
4673 let global = state_ref.global.borrow();
4674 global.heap.unpause();
4675 let roots = CollectRoots {
4676 global: &*global,
4677 thread: state_ref,
4678 };
4679 let hook = |marker: &mut lua_gc::Marker| {
4680 atomic_ran.set(true);
4681 alive_ids.borrow_mut().reserve(weak_table_capacity);
4682 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4683 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4684 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4685 live_interned_ids.borrow_mut().reserve(interned_capacity);
4686 trace_reachable_threads(&*global, global.current_thread_id, marker);
4687 close_open_upvalues_for_unreachable_threads(&*global, marker);
4688 loop {
4689 let visited_before = marker.visited_count();
4690 for t in &weak_tables_snapshot.ephemeron {
4691 let t_id = t.identity();
4692 if !marker.is_visited(t_id) {
4693 continue;
4694 }
4695 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4696 for v in &to_mark {
4697 v.trace(marker);
4698 }
4699 }
4700 marker.drain_gray_queue();
4701 if marker.visited_count() == visited_before {
4702 break;
4703 }
4704 }
4705 for pf in &pending_snapshot {
4706 if !marker.is_visited(pf.identity()) {
4707 pf.mark(marker);
4708 finalizing_ids.borrow_mut().insert(pf.identity());
4709 newly_unreachable.borrow_mut().push(pf.clone());
4710 }
4711 }
4712 marker.drain_gray_queue();
4713 loop {
4714 let visited_before = marker.visited_count();
4715 for t in &weak_tables_snapshot.ephemeron {
4716 let t_id = t.identity();
4717 if !marker.is_visited(t_id) {
4718 continue;
4719 }
4720 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4721 for v in &to_mark {
4722 v.trace(marker);
4723 }
4724 }
4725 marker.drain_gray_queue();
4726 if marker.visited_count() == visited_before {
4727 break;
4728 }
4729 }
4730 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4731 let id = t.identity();
4732 if marker.is_visited(id) {
4733 let to_mark = {
4734 let finalizing = finalizing_ids.borrow();
4735 t.prune_weak_dead_with(&|id| marker.is_visited(id), &|id| {
4736 marker.is_visited(id) && !finalizing.contains(&id)
4737 })
4738 };
4739 for v in &to_mark {
4740 v.trace(marker);
4741 }
4742 alive_ids.borrow_mut().insert(id);
4743 }
4744 }
4745 marker.drain_gray_queue();
4746 {
4747 let mut alive = alive_thread_ids.borrow_mut();
4748 for (id, entry) in global.threads.iter() {
4749 if thread_entry_marked_alive(marker, *id, entry) {
4750 alive.insert(*id);
4751 }
4752 }
4753 }
4754 record_live_interned_strings(&*global, marker, &live_interned_ids);
4755 };
4756 let budget = StepBudget::from_work(work_units);
4757 if let Some(target) = stop_target {
4758 global
4759 .heap
4760 .incremental_run_until_state_with_post_mark(&roots, target, work_units, hook)
4761 } else {
4762 global
4763 .heap
4764 .incremental_step_with_post_mark(&roots, budget, hook)
4765 }
4766 };
4767
4768 if atomic_ran.get() {
4769 let alive_set = alive_ids.into_inner();
4770 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4771 let alive_thread_ids = alive_thread_ids.into_inner();
4772 let live_interned_ids = live_interned_ids.into_inner();
4773 let mut g = state_ref.global.borrow_mut();
4774 retain_live_interned_strings(&mut *g, live_interned_ids);
4775 g.weak_tables_registry.retain_identities(&alive_set);
4776 let main_thread_id = g.main_thread_id;
4777 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4778 g.cross_thread_upvals
4779 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4780 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4781 for object in &promoted {
4782 if let Some(ptr) = object.heap_ptr() {
4783 g.heap.move_finobj_to_tobefnz(ptr);
4784 }
4785 }
4786 }
4787
4788 let mut paused = matches!(outcome, StepOutcome::Paused);
4789 if target.is_none()
4790 && self._state.global().heap.gc_state() == lua_gc::GcState::CallFin
4791 && !self._state.global().finalizers.has_to_be_finalized()
4792 {
4793 paused = self._state.global().heap.finish_callfin_phase();
4794 }
4795
4796 paused
4797 }
4798
4799 pub fn prune_weak_tables_mark_only(&self) {
4806 use lua_gc::Trace;
4807 let state_ref: &LuaState = &*self._state;
4808
4809 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4810 let mut g = state_ref.global.borrow_mut();
4811 g.weak_tables_registry.live_snapshot_by_kind()
4812 };
4813 let interned_capacity = {
4814 let g = state_ref.global.borrow();
4815 g.interned_lt.len()
4816 };
4817
4818 let live_interned_ids: std::cell::RefCell<Vec<usize>> = std::cell::RefCell::new(Vec::new());
4819
4820 {
4821 let global = state_ref.global.borrow();
4822 global.heap.unpause();
4823 let roots = CollectRoots {
4824 global: &*global,
4825 thread: state_ref,
4826 };
4827 let hook = |marker: &mut lua_gc::Marker| {
4828 live_interned_ids.borrow_mut().reserve(interned_capacity);
4829 trace_reachable_threads(&*global, global.current_thread_id, marker);
4830 loop {
4831 let visited_before = marker.visited_count();
4832 for t in &weak_tables_snapshot.ephemeron {
4833 let t_id = t.identity();
4834 if !marker.is_visited(t_id) {
4835 continue;
4836 }
4837 let to_mark = t.ephemeron_values_to_mark(&|id| marker.is_visited(id));
4838 for v in &to_mark {
4839 v.trace(marker);
4840 }
4841 }
4842 marker.drain_gray_queue();
4843 if marker.visited_count() == visited_before {
4844 break;
4845 }
4846 }
4847 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4848 if marker.is_visited(t.identity()) {
4849 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4850 for v in &to_mark {
4851 v.trace(marker);
4852 }
4853 }
4854 }
4855 marker.drain_gray_queue();
4856 record_live_interned_strings(&*global, marker, &live_interned_ids);
4857 };
4858 global.heap.mark_only_with_post_mark(&roots, hook);
4859 }
4860
4861 let live_interned_ids = live_interned_ids.into_inner();
4862 let mut g = state_ref.global.borrow_mut();
4863 retain_live_interned_strings(&mut *g, live_interned_ids);
4864 }
4865
4866 pub fn change_mode(&self, mode: GcKind) {
4868 let old = self._state.global().gckind;
4869 if old == mode as u8 {
4870 self._state.global_mut().lastatomic = 0;
4871 return;
4872 }
4873 match mode {
4874 GcKind::Generational => {
4875 self.enter_generational_mode();
4876 }
4877 GcKind::Incremental => {
4878 self.enter_incremental_mode();
4879 }
4880 }
4881 }
4882
4883 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4886
4887 pub fn free_all_objects(&self) {
4891 }
4893
4894 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4898 let g = self._state.global();
4899 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4900 }
4901
4902 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4906 let g = self._state.global();
4907 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4908 }
4909
4910 pub fn table_barrier_back(&self, p: &GcRef<LuaTable>, v: &LuaValue) {
4912 let g = self._state.global();
4913 barrier_lua_value(&g.heap, *p, v, g.is_gen_mode(), BarrierKind::Backward);
4914 }
4915
4916 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4920 let g = self._state.global();
4921 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4922 }
4923
4924 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4927 let g = self._state.global();
4928 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4929 }
4930}
4931
4932fn make_seed() -> u32 {
4941 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4942 {
4943 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
4944 }
4945
4946 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
4947 {
4948 use std::time::{SystemTime, UNIX_EPOCH};
4949 let t = SystemTime::now()
4950 .duration_since(UNIX_EPOCH)
4951 .map(|d| d.as_secs() as u32)
4952 .unwrap_or(0);
4953
4954 crate::string::hash_bytes(&t.to_le_bytes(), t)
4962 }
4963}
4964
4965pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
4979 let tb = g.total_bytes() as isize;
4980 debug_assert!(tb > 0);
4981 if debt < tb.saturating_sub(isize::MAX) {
4983 debt = tb - isize::MAX;
4984 }
4985 g.gc_debt = debt;
4986}
4987
4988pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
4998 let _ = (_state, _limit);
4999 LUAI_MAXCCALLS as i32
5000}
5001
5002pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
5019 debug_assert!(
5020 state.call_info[state.ci.0 as usize].next.is_none(),
5021 "extend_ci: current ci already has a cached next frame"
5022 );
5023
5024 let current_idx = state.ci;
5025 let new_idx = CallInfoIdx(state.call_info.len() as u32);
5027
5028 state.call_info.push(CallInfo {
5029 previous: Some(current_idx),
5030 next: None,
5031 u: CallInfoFrame::lua_default(),
5032 ..CallInfo::default()
5033 });
5034
5035 state.call_info[current_idx.0 as usize].next = Some(new_idx);
5036
5037 state.nci += 1;
5038
5039 new_idx
5040}
5041
5042fn free_ci(state: &mut LuaState) {
5063 let ci_idx = state.ci.0 as usize;
5064
5065 let mut next_opt = state.call_info[ci_idx].next.take();
5066
5067 while let Some(idx) = next_opt {
5068 next_opt = state.call_info[idx.0 as usize].next;
5069 state.nci = state.nci.saturating_sub(1);
5070 }
5071
5072 state.call_info.truncate(ci_idx + 1);
5075}
5076
5077pub(crate) fn shrink_ci(state: &mut LuaState) {
5104 let ci_idx = state.ci.0 as usize;
5105
5106 if state.call_info[ci_idx].next.is_none() {
5107 return;
5108 }
5109
5110 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
5111 if free_count <= 1 {
5112 return;
5113 }
5114
5115 let keep = free_count / 2;
5119 let removed = free_count - keep;
5120 let new_len = ci_idx + 1 + keep;
5121 state.call_info.truncate(new_len);
5122 state.nci = state.nci.saturating_sub(removed as u32);
5123
5124 if let Some(last) = state.call_info.last_mut() {
5126 last.next = None;
5127 }
5128}
5129
5130pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
5142 if state.c_calls() == LUAI_MAXCCALLS {
5145 return Err(LuaError::runtime(format_args!("C stack overflow")));
5146 }
5147 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
5149 return Err(LuaError::with_status(LuaStatus::ErrErr));
5150 }
5151 Ok(())
5152}
5153
5154pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
5165 state.n_ccalls += 1;
5166 if state.c_calls() >= LUAI_MAXCCALLS {
5168 check_c_stack(state)?;
5169 }
5170 Ok(())
5171}
5172
5173fn stack_init(thread: &mut LuaState) {
5179 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
5181 thread.stack = vec![StackValue::default(); total_slots];
5182
5183 thread.tbclist = Vec::new();
5187
5188 thread.top = StackIdx(0);
5193
5194 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
5195
5196 let base_ci = CallInfo {
5197 func: StackIdx(0),
5198 top: StackIdx(1 + LUA_MINSTACK as u32),
5199 previous: None,
5200 next: None,
5201 callstatus: CIST_C,
5202 call_metamethods: 0,
5203 nresults: 0,
5204 u: CallInfoFrame::c_default(),
5205 u2: CallInfoExtra::default(),
5206 };
5207
5208 if thread.call_info.is_empty() {
5209 thread.call_info.push(base_ci);
5210 } else {
5211 thread.call_info[0] = base_ci;
5212 thread.call_info.truncate(1);
5213 }
5214
5215 thread.stack[0] = StackValue {
5216 val: LuaValue::Nil,
5217 tbc_delta: 0,
5218 };
5219
5220 thread.top = StackIdx(1);
5221
5222 thread.ci = CallInfoIdx(0);
5223}
5224
5225fn free_stack(state: &mut LuaState) {
5226 if state.stack.is_empty() {
5227 return;
5228 }
5229 state.ci = CallInfoIdx(0);
5230 free_ci(state);
5231 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
5232 state.stack.clear();
5234 state.stack.shrink_to_fit();
5235}
5236
5237fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
5238 let registry = state.new_table();
5240
5241 state.global_mut().l_registry = LuaValue::Table(registry.clone());
5243
5244 let globals = state.new_table();
5264 state.global_mut().globals = LuaValue::Table(globals);
5265 let loaded = state.new_table();
5266 state.global_mut().loaded = LuaValue::Table(loaded);
5267
5268 Ok(())
5269}
5270
5271fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
5272 stack_init(state);
5273 init_registry(state)?;
5274 crate::string::init(state)?;
5275 crate::tagmethods::init(state)?;
5276 state.global_mut().gcstp = 0;
5278 state.global().heap.unpause();
5279 state.global_mut().nilvalue = LuaValue::Nil;
5282 Ok(())
5284}
5285
5286fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
5287 thread.global = global;
5288 thread.stack = Vec::new();
5289 thread.call_info = Vec::new();
5290 thread.ci = CallInfoIdx(0);
5293 thread.nci = 0;
5294 thread.n_ccalls = 0;
5298 thread.hook = None;
5299 thread.hookmask = 0;
5300 thread.basehookcount = 0;
5301 thread.allowhook = true;
5302 thread.hookcount = thread.basehookcount;
5304
5305 {
5310 let (active, interval) = {
5311 let g = thread.global.borrow();
5312 (g.sandbox_active(), g.sandbox.interval.get())
5313 };
5314 if active {
5315 thread.hookmask = SANDBOX_COUNT_MASK;
5316 thread.basehookcount = interval;
5317 thread.hookcount = interval;
5318 }
5319 }
5320 thread.openupval = Vec::new();
5321 thread.status = LuaStatus::Ok as u8;
5322 thread.errfunc = 0;
5323 thread.oldpc = 0;
5324 thread.gc_check_needed = true;
5325}
5326
5327fn close_state(state: &mut LuaState) {
5328 let is_complete = state.global().is_complete();
5329
5330 if !is_complete {
5331 state.gc().free_all_objects();
5333 } else {
5334 state.ci = CallInfoIdx(0);
5335 state.gc().free_all_objects();
5338 }
5340
5341 state.global_mut().strt = StringPool::default();
5343
5344 free_stack(state);
5345
5346 }
5351
5352pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
5381 state.gc().check_step();
5382
5383 let global_rc = state.global_rc();
5388 let hookmask = state.hookmask;
5389 let basehookcount = state.basehookcount;
5390
5391 let reserved_id = {
5392 let mut g = state.global_mut();
5393 let id = g.next_thread_id;
5394 g.next_thread_id += 1;
5395 id
5396 };
5397
5398 let mut new_thread = LuaState {
5399 status: LuaStatus::Ok as u8,
5400 allowhook: true,
5401 nci: 0,
5402 top: StackIdx(0),
5403 stack_last: StackIdx(0),
5404 stack: Vec::new(),
5405 ci: CallInfoIdx(0),
5406 call_info: Vec::new(),
5407 openupval: Vec::new(),
5408 tbclist: Vec::new(),
5409 global: global_rc.clone(),
5410 hook: None,
5411 hookmask: 0,
5412 basehookcount: 0,
5413 hookcount: 0,
5414 errfunc: 0,
5415 n_ccalls: 0,
5416 oldpc: 0,
5417 marked: 0,
5418 cached_thread_id: reserved_id,
5419 gc_check_needed: false,
5420 };
5421
5422 preinit_thread(&mut new_thread, global_rc);
5423
5424 new_thread.hookmask = hookmask;
5425 new_thread.basehookcount = basehookcount;
5426 new_thread.reset_hook_count();
5429
5430 stack_init(&mut new_thread);
5436
5437 if let Some(body) = initial_body {
5438 new_thread.push(body);
5439 }
5440
5441 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
5442
5443 let value = {
5444 let mut g = state.global_mut();
5445 let id = reserved_id;
5446 let value = GcRef::new(lua_types::value::LuaThread::new(id));
5447 g.threads.insert(
5448 id,
5449 ThreadRegistryEntry {
5450 state: thread_ref,
5451 value: value.clone(),
5452 },
5453 );
5454 value
5455 };
5456
5457 state.push(LuaValue::Thread(value));
5458
5459 Ok(())
5460}
5461
5462pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
5484 state.ci = CallInfoIdx(0);
5485 let ci_idx = 0usize;
5486
5487 if !state.stack.is_empty() {
5489 state.stack[0].val = LuaValue::Nil;
5490 }
5491
5492 state.call_info[ci_idx].func = StackIdx(0);
5493 state.call_info[ci_idx].call_metamethods = 0;
5494 state.call_info[ci_idx].callstatus = CIST_C;
5495
5496 let mut status = if status == LuaStatus::Yield as i32 {
5497 LuaStatus::Ok as i32
5498 } else {
5499 status
5500 };
5501
5502 state.status = LuaStatus::Ok as u8;
5503
5504 let close_status = crate::do_::close_protected(state, StackIdx(1), LuaStatus::from_raw(status));
5505 status = close_status as i32;
5506
5507 if status != LuaStatus::Ok as i32 {
5508 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
5509 } else {
5510 state.top = StackIdx(1);
5511 }
5512
5513 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
5514 state.call_info[ci_idx].top = new_ci_top;
5515
5516 let needed = new_ci_top.0 as usize;
5519 if state.stack.len() < needed {
5520 state.stack.resize(needed, StackValue::default());
5521 }
5522
5523 status
5524}
5525
5526pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
5540 state.n_ccalls = match from {
5542 Some(f) => f.c_calls(),
5543 None => 0,
5544 };
5545 let current_status = state.status as i32;
5546 let result = reset_thread(state, current_status);
5547 result
5548}
5549
5550pub fn reset_thread_api(state: &mut LuaState) -> i32 {
5559 close_thread(state, None)
5560}
5561
5562pub fn new_state() -> Option<LuaState> {
5598 let placeholder_str = GcRef::new(LuaString::placeholder());
5607
5608 let initial_white = 1u8 << WHITE0BIT;
5610
5611 let global = GlobalState {
5615 parser_hook: None,
5616 cli_argv: None,
5617 cli_preload: None,
5618 lua_version: lua_types::LuaVersion::default(),
5619 file_loader_hook: None,
5620 file_open_hook: None,
5621 stdout_hook: None,
5622 stderr_hook: None,
5623 stdin_hook: None,
5624 env_hook: None,
5625 unix_time_hook: None,
5626 cpu_clock_hook: None,
5627 local_offset_hook: None,
5628 entropy_hook: None,
5629 temp_name_hook: None,
5630 popen_hook: None,
5631 file_remove_hook: None,
5632 file_rename_hook: None,
5633 os_execute_hook: None,
5634 dynlib_load_hook: None,
5635 dynlib_symbol_hook: None,
5636 dynlib_unload_hook: None,
5637 sandbox: SandboxLimits::default(),
5638 gc_debt: 0,
5639 gc_estimate: 0,
5640 lastatomic: 0,
5641 strt: StringPool::default(),
5642 l_registry: LuaValue::Nil,
5643 external_roots: ExternalRootSet::default(),
5644 globals: LuaValue::Nil,
5645 loaded: LuaValue::Nil,
5646 nilvalue: LuaValue::Int(0),
5647 seed: make_seed(),
5648 currentwhite: initial_white,
5649 gcstate: GCS_PAUSE,
5650 gckind: GcKind::Incremental as u8,
5652 gcstopem: false,
5653 genminormul: LUAI_GENMINORMUL,
5654 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5656 gcstp: GCSTPGC,
5657 gcemergency: false,
5658 gcpause: (LUAI_GCPAUSE / 4) as u8,
5659 gcstepmul: (LUAI_GCMUL / 4) as u8,
5660 gcstepsize: LUAI_GCSTEPSIZE,
5661 gc55_params: [20, 50, 68, 250, 200, 9600],
5664 sweepgc_cursor: 0,
5665 weak_tables_registry: lua_gc::WeakRegistry::default(),
5666 finalizers: lua_gc::FinalizerRegistry::default(),
5667 gc_finalizer_error: None,
5668 twups: Vec::new(),
5669 panic: None,
5670 mainthread: None,
5671 threads: std::collections::HashMap::new(),
5672 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5673 current_thread_id: 0,
5674 closing_thread_id: None,
5675 main_thread_id: 0,
5676 next_thread_id: 1,
5677 memerrmsg: placeholder_str.clone(),
5678 tmname: Vec::new(),
5679 mt: std::array::from_fn(|_| None),
5680 strcache: std::array::from_fn(|_| std::array::from_fn(|_| placeholder_str.clone())),
5681 interned_lt: InternedStringMap::default(),
5682 warnf: None,
5683 warn_mode: WarnMode::Off,
5684 test_warn_enabled: false,
5685 test_warn_on: false,
5686 test_warn_mode: TestWarnMode::Normal,
5687 test_warn_last_to_cont: false,
5688 test_warn_buffer: Vec::new(),
5689 c_functions: Vec::new(),
5690 heap: lua_gc::Heap::new(),
5691 cross_thread_upvals: std::collections::HashMap::new(),
5692 suspended_parent_stacks: Vec::new(),
5693 suspended_parent_open_upvals: Vec::new(),
5694 };
5695
5696 let global_rc = Rc::new(RefCell::new(global));
5697
5698 let initial_marked = initial_white;
5700
5701 let mut main_thread = LuaState {
5702 status: LuaStatus::Ok as u8,
5703 allowhook: true,
5704 nci: 0,
5705 top: StackIdx(0),
5706 stack_last: StackIdx(0),
5707 stack: Vec::new(),
5708 ci: CallInfoIdx(0),
5709 call_info: Vec::new(),
5710 openupval: Vec::new(),
5711 tbclist: Vec::new(),
5712 global: global_rc.clone(),
5713 hook: None,
5714 hookmask: 0,
5715 basehookcount: 0,
5716 hookcount: 0,
5717 errfunc: 0,
5718 n_ccalls: 0,
5719 oldpc: 0,
5720 marked: initial_marked,
5721 cached_thread_id: 0,
5722 gc_check_needed: false,
5723 };
5724
5725 preinit_thread(&mut main_thread, global_rc.clone());
5726
5727 main_thread.inc_nny();
5729
5730 match lua_open(&mut main_thread) {
5740 Ok(()) => {}
5741 Err(_) => {
5742 close_state(&mut main_thread);
5743 return None;
5744 }
5745 }
5746
5747 Some(main_thread)
5748}
5749
5750pub fn close(mut state: LuaState) {
5766 close_state(&mut state);
5771}
5772
5773pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5783 let test_warn_enabled = state.global().test_warn_enabled;
5784 if test_warn_enabled {
5785 test_warn(state, msg, to_cont);
5786 return;
5787 }
5788
5789 let has_warnf = state.global().warnf.is_some();
5798 if has_warnf {
5799 let mut warnf = state.global_mut().warnf.take();
5801 if let Some(ref mut f) = warnf {
5802 f(msg, to_cont);
5803 }
5804 state.global_mut().warnf = warnf;
5806 return;
5807 }
5808 default_warn(state, msg, to_cont);
5809}
5810
5811fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5812 let is_control = {
5813 let g = state.global();
5814 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5815 };
5816 if is_control {
5817 let mut g = state.global_mut();
5818 match &msg[1..] {
5819 b"off" => g.test_warn_on = false,
5820 b"on" => g.test_warn_on = true,
5821 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5822 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5823 b"store" => g.test_warn_mode = TestWarnMode::Store,
5824 _ => {}
5825 }
5826 return;
5827 }
5828
5829 let finished = {
5830 let mut g = state.global_mut();
5831 g.test_warn_last_to_cont = to_cont;
5832 g.test_warn_buffer.extend_from_slice(msg);
5833 if to_cont {
5834 None
5835 } else {
5836 Some((
5837 std::mem::take(&mut g.test_warn_buffer),
5838 g.test_warn_mode,
5839 g.test_warn_on,
5840 ))
5841 }
5842 };
5843
5844 let Some((message, mode, warn_on)) = finished else {
5845 return;
5846 };
5847 match mode {
5848 TestWarnMode::Normal => {
5849 if warn_on && message.first() == Some(&b'#') {
5850 write_warning_message(&message);
5851 }
5852 }
5853 TestWarnMode::Allow => {
5854 if warn_on {
5855 write_warning_message(&message);
5856 }
5857 }
5858 TestWarnMode::Store => {
5859 if let Ok(s) = state.intern_str(&message) {
5860 state.push(LuaValue::Str(s));
5861 let _ = crate::api::set_global(state, b"_WARN");
5862 }
5863 }
5864 }
5865}
5866
5867fn write_warning_message(message: &[u8]) {
5868 use std::io::Write;
5869 let stderr = std::io::stderr();
5870 let mut h = stderr.lock();
5871 let _ = h.write_all(b"Lua warning: ");
5872 let _ = h.write_all(message);
5873 let _ = h.write_all(b"\n");
5874}
5875
5876fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5881 use std::io::Write;
5882 if !to_cont && msg.first() == Some(&b'@') {
5884 match &msg[1..] {
5885 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5886 b"on" => state.global_mut().warn_mode = WarnMode::On,
5887 _ => {}
5888 }
5889 return;
5890 }
5891 let mode = state.global().warn_mode;
5892 match mode {
5893 WarnMode::Off => {}
5894 WarnMode::On | WarnMode::Cont => {
5895 let stderr = std::io::stderr();
5896 let mut h = stderr.lock();
5897 if mode == WarnMode::On {
5898 let _ = h.write_all(b"Lua warning: ");
5899 }
5900 let _ = h.write_all(msg);
5901 if to_cont {
5902 state.global_mut().warn_mode = WarnMode::Cont;
5903 } else {
5904 let _ = h.write_all(b"\n");
5905 state.global_mut().warn_mode = WarnMode::On;
5906 }
5907 }
5908 }
5909}
5910
5911#[cfg(test)]
5912mod tests {
5913 use super::*;
5914
5915 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5916 Ok(0)
5917 }
5918
5919 #[test]
5920 fn external_root_keys_reject_stale_slot_after_reuse() {
5921 let mut roots = ExternalRootSet::default();
5922
5923 let first = roots.insert(LuaValue::Int(1));
5924 assert_eq!(roots.len(), 1);
5925 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5926
5927 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5928 assert!(roots.get(first).is_none());
5929 assert!(roots.remove(first).is_none());
5930 assert_eq!(roots.len(), 0);
5931 assert_eq!(roots.vacant_len(), 1);
5932 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5933 assert!(roots.is_empty());
5934
5935 let second = roots.insert(LuaValue::Int(2));
5936 assert_eq!(first.index, second.index);
5937 assert_ne!(first, second);
5938 assert!(roots.get(first).is_none());
5939 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5940 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5941 }
5942
5943 #[test]
5944 fn external_roots_keep_heap_value_alive_until_unrooted() {
5945 let mut state = new_state().expect("state should initialize");
5946 let _heap_guard = {
5947 let g = state.global();
5948 lua_gc::HeapGuard::push(&g.heap)
5949 };
5950
5951 let table = state.new_table();
5952 assert_eq!(state.global().heap.allgc_count(), 1);
5953
5954 let key = state.external_root_value(LuaValue::Table(table));
5955 state.gc().full_collect();
5956 assert_eq!(state.global().heap.allgc_count(), 1);
5957 assert_eq!(state.global().external_roots.len(), 1);
5958
5959 assert!(state.external_unroot_value(key).is_some());
5960 state.gc().full_collect();
5961 assert_eq!(state.global().heap.allgc_count(), 0);
5962 assert!(state.global().external_roots.is_empty());
5963 }
5964
5965 #[test]
5966 fn table_buffer_accounting_refunds_on_sweep() {
5967 let mut state = new_state().expect("state should initialize");
5968 let _heap_guard = {
5969 let g = state.global();
5970 lua_gc::HeapGuard::push(&g.heap)
5971 };
5972
5973 let table = state.new_table();
5974 let key = state.external_root_value(LuaValue::Table(table));
5975 let header_bytes = state.global().heap.bytes_used();
5976 assert!(header_bytes > 0);
5977
5978 for i in 1..=128 {
5979 table
5980 .raw_set_int(&mut state, i, LuaValue::Int(i))
5981 .expect("integer table insert should succeed");
5982 }
5983 let grown_bytes = state.global().heap.bytes_used();
5984 assert!(
5985 grown_bytes > header_bytes,
5986 "table array/hash buffer growth must be charged to the GC heap"
5987 );
5988
5989 state.gc().full_collect();
5990 assert_eq!(
5991 state.global().heap.bytes_used(),
5992 grown_bytes,
5993 "rooted table buffer bytes should remain charged after collection"
5994 );
5995
5996 assert!(state.external_unroot_value(key).is_some());
5997 state.gc().full_collect();
5998 assert_eq!(state.global().heap.bytes_used(), 0);
5999 assert_eq!(state.global().heap.allgc_count(), 0);
6000 }
6001
6002 #[test]
6003 fn userdata_buffer_accounting_refunds_on_sweep() {
6004 let mut state = new_state().expect("state should initialize");
6005 let _heap_guard = {
6006 let g = state.global();
6007 lua_gc::HeapGuard::push(&g.heap)
6008 };
6009
6010 let payload_len = 4096;
6011 let userdata = state
6012 .new_userdata_typed(b"accounting", payload_len, 3)
6013 .expect("userdata allocation should succeed");
6014 state.pop_n(1);
6015 let key = state.external_root_value(LuaValue::UserData(userdata));
6016 let allocated_bytes = state.global().heap.bytes_used();
6017 assert!(
6018 allocated_bytes > payload_len,
6019 "userdata payload bytes must be charged to the GC heap"
6020 );
6021
6022 state.gc().full_collect();
6023 assert_eq!(
6024 state.global().heap.bytes_used(),
6025 allocated_bytes,
6026 "rooted userdata payload bytes should remain charged after collection"
6027 );
6028
6029 assert!(state.external_unroot_value(key).is_some());
6030 state.gc().full_collect();
6031 assert_eq!(state.global().heap.bytes_used(), 0);
6032 assert_eq!(state.global().heap.allgc_count(), 0);
6033 }
6034
6035 #[test]
6036 fn cclosure_upvalue_accounting_refunds_on_sweep() {
6037 let mut state = new_state().expect("state should initialize");
6038 let _heap_guard = {
6039 let g = state.global();
6040 lua_gc::HeapGuard::push(&g.heap)
6041 };
6042
6043 let nupvalues = 64;
6044 for i in 0..nupvalues {
6045 state.push(LuaValue::Int(i as i64));
6046 }
6047 crate::api::push_cclosure(&mut state, test_noop_cclosure, nupvalues as i32)
6048 .expect("C closure creation should succeed");
6049 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6050 panic!("expected heavy C closure");
6051 };
6052 let expected_payload = ccl.buffer_bytes();
6053 let key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6054 state.pop_n(1);
6055 let allocated_bytes = state.global().heap.bytes_used();
6056 assert!(
6057 allocated_bytes >= expected_payload,
6058 "C closure upvalue vector bytes must be charged to the GC heap"
6059 );
6060
6061 state.gc().full_collect();
6062 assert_eq!(
6063 state.global().heap.bytes_used(),
6064 allocated_bytes,
6065 "rooted C closure payload bytes should remain charged after collection"
6066 );
6067
6068 assert!(state.external_unroot_value(key).is_some());
6069 state.gc().full_collect();
6070 assert_eq!(state.global().heap.bytes_used(), 0);
6071 assert_eq!(state.global().heap.allgc_count(), 0);
6072 }
6073
6074 #[test]
6075 fn proto_and_lclosure_accounting_refunds_on_sweep() {
6076 let mut state = new_state().expect("state should initialize");
6077 let _heap_guard = {
6078 let g = state.global();
6079 lua_gc::HeapGuard::push(&g.heap)
6080 };
6081
6082 let mut proto = LuaProto::placeholder();
6083 proto.code = vec![lua_types::opcode::Instruction(0); 2048];
6084 proto.lineinfo = vec![0; 2048];
6085 proto.k = vec![LuaValue::Int(1); 512];
6086 let expected_proto_payload = proto.buffer_bytes();
6087 let proto = GcRef::new(proto);
6088 proto.account_buffer(expected_proto_payload as isize);
6089
6090 let closure = state.new_lclosure(proto, 16);
6091 let expected_closure_payload = closure.buffer_bytes();
6092 let key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6093 let allocated_bytes = state.global().heap.bytes_used();
6094 assert!(
6095 allocated_bytes >= expected_proto_payload + expected_closure_payload,
6096 "proto and Lua closure vector bytes must be charged to the GC heap"
6097 );
6098
6099 state.gc().full_collect();
6100 assert_eq!(
6101 state.global().heap.bytes_used(),
6102 allocated_bytes,
6103 "rooted proto and Lua closure payload bytes should remain charged after collection"
6104 );
6105
6106 assert!(state.external_unroot_value(key).is_some());
6107 state.gc().full_collect();
6108 assert_eq!(state.global().heap.bytes_used(), 0);
6109 assert_eq!(state.global().heap.allgc_count(), 0);
6110 }
6111
6112 #[test]
6113 fn string_buffer_accounting_refunds_on_sweep() {
6114 let mut state = new_state().expect("state should initialize");
6115 let _heap_guard = {
6116 let g = state.global();
6117 lua_gc::HeapGuard::push(&g.heap)
6118 };
6119
6120 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
6121 let string = state
6122 .intern_str(&payload)
6123 .expect("long string should allocate");
6124 let key = state.external_root_value(LuaValue::Str(string));
6125 let allocated_bytes = state.global().heap.bytes_used();
6126 assert!(
6127 allocated_bytes > payload.len(),
6128 "long string backing bytes must be charged to the GC heap"
6129 );
6130
6131 state.gc().full_collect();
6132 assert_eq!(
6133 state.global().heap.bytes_used(),
6134 allocated_bytes,
6135 "rooted string buffer bytes should remain charged after collection"
6136 );
6137
6138 assert!(state.external_unroot_value(key).is_some());
6139 state.gc().full_collect();
6140 assert_eq!(state.global().heap.bytes_used(), 0);
6141 assert_eq!(state.global().heap.allgc_count(), 0);
6142 }
6143
6144 #[test]
6145 fn interned_short_string_cache_does_not_root_unreferenced_string() {
6146 let mut state = new_state().expect("state should initialize");
6147 let _heap_guard = {
6148 let g = state.global();
6149 lua_gc::HeapGuard::push(&g.heap)
6150 };
6151
6152 let payload = b"weak-cache-probe-a";
6153 let string = state
6154 .intern_str(payload)
6155 .expect("short string should intern");
6156 let id = string.identity();
6157 assert!(state.global().interned_lt.contains_key(&payload[..]));
6158 assert!(state.global().heap.allocation_token(id).is_some());
6159
6160 state.gc().full_collect();
6161 assert!(!state.global().interned_lt.contains_key(&payload[..]));
6162 assert_eq!(state.global().heap.allocation_token(id), None);
6163 }
6164
6165 #[test]
6166 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
6167 let mut state = new_state().expect("state should initialize");
6168 let _heap_guard = {
6169 let g = state.global();
6170 lua_gc::HeapGuard::push(&g.heap)
6171 };
6172
6173 let payload = b"weak-cache-probe-b";
6174 let string = state
6175 .intern_str(payload)
6176 .expect("short string should intern");
6177 let id = string.identity();
6178 let key = state.external_root_value(LuaValue::Str(string));
6179
6180 state.gc().full_collect();
6181 assert!(state.global().interned_lt.contains_key(&payload[..]));
6182 assert!(state.global().heap.allocation_token(id).is_some());
6183
6184 assert!(state.external_unroot_value(key).is_some());
6185 state.gc().full_collect();
6186 assert!(!state.global().interned_lt.contains_key(&payload[..]));
6187 assert_eq!(state.global().heap.allocation_token(id), None);
6188 }
6189
6190 #[test]
6191 fn gc_phase_predicates_follow_heap_state() {
6192 let mut state = new_state().expect("state should initialize");
6193 let _heap_guard = {
6194 let g = state.global();
6195 lua_gc::HeapGuard::push(&g.heap)
6196 };
6197
6198 {
6199 let mut g = state.global_mut();
6200 g.gckind = GcKind::Incremental as u8;
6201 g.lastatomic = 0;
6202 assert!(!g.is_gen_mode());
6203 g.lastatomic = 1;
6204 assert!(g.is_gen_mode());
6205 g.lastatomic = 0;
6206 }
6207
6208 let mut roots = Vec::new();
6209 for _ in 0..16 {
6210 let table = state.new_table();
6211 roots.push(state.external_root_value(LuaValue::Table(table)));
6212 }
6213
6214 let mut saw_keep = false;
6215 let mut saw_sweep = false;
6216 for _ in 0..128 {
6217 state.gc().incremental_step(1);
6218 let g = state.global();
6219 let heap_state = g.heap.gc_state();
6220 assert_eq!(g.keep_invariant(), heap_state.is_invariant());
6221 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
6222 saw_keep |= g.keep_invariant();
6223 saw_sweep |= g.is_sweep_phase();
6224 if heap_state.is_pause() && saw_keep && saw_sweep {
6225 break;
6226 }
6227 }
6228
6229 assert!(
6230 saw_keep,
6231 "incremental cycle should expose an invariant phase"
6232 );
6233 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
6234
6235 for key in roots {
6236 assert!(state.external_unroot_value(key).is_some());
6237 }
6238 state.gc().full_collect();
6239 }
6240
6241 #[test]
6242 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
6243 let mut state = new_state().expect("state should initialize");
6244 let _heap_guard = {
6245 let g = state.global();
6246 lua_gc::HeapGuard::push(&g.heap)
6247 };
6248
6249 let parent = state.new_table();
6250 let parent_key = state.external_root_value(LuaValue::Table(parent));
6251 state.gc().incremental_step(1);
6252 assert!(
6253 state.global().keep_invariant(),
6254 "test setup should leave the parent marked during an active cycle"
6255 );
6256
6257 let child = state.new_table();
6258 let parent_value = LuaValue::Table(parent);
6259 let child_value = LuaValue::Table(child);
6260 parent
6261 .raw_set_int(&mut state, 1, child_value)
6262 .expect("table store should succeed");
6263 state.gc_barrier_back(&parent_value, &child_value);
6264
6265 for _ in 0..128 {
6266 if state.gc().incremental_step(1) {
6267 break;
6268 }
6269 }
6270
6271 assert_eq!(state.global().heap.allgc_count(), 2);
6272 assert_eq!(
6273 parent.get_int(1).as_table().map(|t| t.identity()),
6274 Some(child.identity())
6275 );
6276
6277 assert!(state.external_unroot_value(parent_key).is_some());
6278 state.gc().full_collect();
6279 assert_eq!(state.global().heap.allgc_count(), 0);
6280 }
6281
6282 #[test]
6283 fn generational_mode_promotes_and_barriers_age_objects() {
6284 let mut state = new_state().expect("state should initialize");
6285 let _heap_guard = {
6286 let g = state.global();
6287 lua_gc::HeapGuard::push(&g.heap)
6288 };
6289
6290 let parent = state.new_table();
6291 let parent_key = state.external_root_value(LuaValue::Table(parent));
6292
6293 state.gc().change_mode(GcKind::Generational);
6294 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
6295 assert_eq!(parent.0.color(), lua_gc::Color::Black);
6296 let majorbase = state.global().gc_estimate;
6297 assert!(majorbase > 0);
6298 assert!(state.global().gc_debt() <= 0);
6299
6300 let child = state.new_table();
6301 let parent_value = LuaValue::Table(parent);
6302 let child_value = LuaValue::Table(child);
6303 parent
6304 .raw_set_int(&mut state, 1, child_value.clone())
6305 .expect("table store should succeed");
6306 state.gc_barrier_back(&parent_value, &child_value);
6307 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
6308 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
6309 assert_eq!(child.0.age(), lua_gc::GcAge::New);
6310
6311 let metatable = state.new_table();
6312 parent.set_metatable(Some(metatable));
6313 state.gc().obj_barrier(&parent, &metatable);
6314 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
6315
6316 assert!(state.gc().generational_step_minor_only());
6317 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
6318 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
6319 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
6320 assert_eq!(state.global().gc_estimate, majorbase);
6321 assert!(state.global().gc_debt() <= 0);
6322
6323 state.gc().change_mode(GcKind::Incremental);
6324 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
6325 assert_eq!(child.0.age(), lua_gc::GcAge::New);
6326 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
6327
6328 assert!(state.external_unroot_value(parent_key).is_some());
6329 state.gc().full_collect();
6330 }
6331
6332 #[test]
6333 fn generational_upvalue_write_barrier_marks_young_child_old0() {
6334 let mut state = new_state().expect("state should initialize");
6335 let _heap_guard = {
6336 let g = state.global();
6337 lua_gc::HeapGuard::push(&g.heap)
6338 };
6339
6340 let proto = state.new_proto();
6341 let closure = state.new_lclosure(proto, 1);
6342 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6343 state.gc().change_mode(GcKind::Generational);
6344 let uv = closure.upval(0);
6345 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
6346
6347 let child = state.new_table();
6348 state
6349 .upvalue_set(&closure, 0, LuaValue::Table(child))
6350 .expect("closed upvalue write should succeed");
6351 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6352
6353 assert!(state.external_unroot_value(closure_key).is_some());
6354 state.gc().full_collect();
6355 }
6356
6357 #[test]
6358 fn cclosure_setupvalue_replaces_upvalue() {
6359 let mut state = new_state().expect("state should initialize");
6360 let _heap_guard = {
6361 let g = state.global();
6362 lua_gc::HeapGuard::push(&g.heap)
6363 };
6364
6365 let first = state.new_table();
6366 state.push(LuaValue::Table(first));
6367 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6368 .expect("C closure creation should succeed");
6369 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6370 panic!("expected heavy C closure");
6371 };
6372
6373 let second = state.new_table();
6374 state.push(LuaValue::Table(second));
6375 let name =
6376 crate::api::setup_value(&mut state, -2, 1).expect("C closure upvalue should exist");
6377
6378 assert!(name.is_empty());
6379 let upvalues = ccl.upvalues.borrow();
6380 let LuaValue::Table(actual) = upvalues[0].clone() else {
6381 panic!("expected table upvalue");
6382 };
6383 assert_eq!(actual.identity(), second.identity());
6384 }
6385
6386 #[test]
6387 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
6388 let mut state = new_state().expect("state should initialize");
6389 let _heap_guard = {
6390 let g = state.global();
6391 lua_gc::HeapGuard::push(&g.heap)
6392 };
6393
6394 state.push(LuaValue::Nil);
6395 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6396 .expect("C closure creation should succeed");
6397 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6398 panic!("expected heavy C closure");
6399 };
6400 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6401
6402 state.gc().change_mode(GcKind::Generational);
6403 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
6404
6405 let child = state.new_table();
6406 state.push(LuaValue::Table(child));
6407 crate::api::setup_value(&mut state, -2, 1).expect("C closure upvalue should exist");
6408
6409 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6410
6411 assert!(state.external_unroot_value(closure_key).is_some());
6412 state.gc().full_collect();
6413 }
6414
6415 #[test]
6416 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
6417 let mut state = new_state().expect("state should initialize");
6418 let _heap_guard = {
6419 let g = state.global();
6420 lua_gc::HeapGuard::push(&g.heap)
6421 };
6422
6423 let proto = state.new_proto();
6424 let closure = state.new_lclosure(proto, 1);
6425 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6426 state.gc().change_mode(GcKind::Generational);
6427 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
6428
6429 let replacement = state.new_upval_closed(LuaValue::Nil);
6430 closure.set_upval(0, replacement);
6431 state.gc().obj_barrier(&closure, &replacement);
6432 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
6433
6434 assert!(state.external_unroot_value(closure_key).is_some());
6435 state.gc().full_collect();
6436 }
6437
6438 #[test]
6439 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
6440 let mut state = new_state().expect("state should initialize");
6441 let _heap_guard = {
6442 let g = state.global();
6443 lua_gc::HeapGuard::push(&g.heap)
6444 };
6445
6446 let mirrored = state.new_table();
6447 state
6448 .global_mut()
6449 .cross_thread_upvals
6450 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
6451
6452 state.gc().full_collect();
6453 assert_eq!(state.global().heap.allgc_count(), 1);
6454
6455 state.global_mut().cross_thread_upvals.clear();
6456 state.gc().full_collect();
6457 assert_eq!(state.global().heap.allgc_count(), 0);
6458 }
6459
6460 #[test]
6461 fn generational_full_collect_promotes_new_survivors_to_old() {
6462 let mut state = new_state().expect("state should initialize");
6463 let _heap_guard = {
6464 let g = state.global();
6465 lua_gc::HeapGuard::push(&g.heap)
6466 };
6467
6468 state.gc().change_mode(GcKind::Generational);
6469 let table = state.new_table();
6470 let table_key = state.external_root_value(LuaValue::Table(table));
6471 assert_eq!(table.0.age(), lua_gc::GcAge::New);
6472
6473 state.gc().full_collect();
6474 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
6475 assert_eq!(table.0.color(), lua_gc::Color::Black);
6476
6477 assert!(state.external_unroot_value(table_key).is_some());
6478 state.gc().full_collect();
6479 }
6480
6481 #[test]
6482 fn gc_packed_params_return_user_visible_values() {
6483 let mut state = new_state().expect("state should initialize");
6484 assert_eq!(
6485 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
6486 200
6487 );
6488 assert_eq!(state.global().gc_pause_param(), 200);
6489 assert_eq!(
6490 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
6491 100
6492 );
6493 assert_eq!(state.global().gc_stepmul_param(), 200);
6494
6495 crate::api::gc(
6496 &mut state,
6497 crate::api::GcArgs::Gen {
6498 minormul: 0,
6499 majormul: 200,
6500 },
6501 );
6502 assert_eq!(state.global().gc_genmajormul_param(), 200);
6503 }
6504
6505 #[test]
6506 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
6507 let mut state = new_state().expect("state should initialize");
6508 let _heap_guard = {
6509 let g = state.global();
6510 lua_gc::HeapGuard::push(&g.heap)
6511 };
6512
6513 let root = state.new_table();
6514 let root_key = state.external_root_value(LuaValue::Table(root));
6515 state.gc().change_mode(GcKind::Generational);
6516
6517 let root_value = LuaValue::Table(root);
6518 for i in 1..=64 {
6519 let child = state.new_table();
6520 let child_value = LuaValue::Table(child);
6521 root.raw_set_int(&mut state, i, child_value.clone())
6522 .expect("table store should succeed");
6523 state.gc_barrier_back(&root_value, &child_value);
6524 }
6525
6526 {
6527 let mut g = state.global_mut();
6528 g.gc_estimate = 1;
6529 set_debt(&mut *g, 1);
6530 }
6531
6532 assert!(state.gc().generational_step());
6533 let g = state.global();
6534 assert!(g.is_gen_mode());
6535 assert!(
6536 g.lastatomic > 0,
6537 "bad major collection should arm stepgenfull"
6538 );
6539 assert!(g.gc_estimate > 1);
6540 assert!(g.gc_debt() <= 0);
6541 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6542 drop(g);
6543
6544 assert!(state.external_unroot_value(root_key).is_some());
6545 state.gc().full_collect();
6546 }
6547
6548 #[test]
6549 fn generational_implicit_step_runs_major_when_heap_threshold_exceeded() {
6550 let mut state = new_state().expect("state should initialize");
6551 let _heap_guard = {
6552 let g = state.global();
6553 lua_gc::HeapGuard::push(&g.heap)
6554 };
6555
6556 let root = state.new_table();
6557 let root_key = state.external_root_value(LuaValue::Table(root));
6558 state.gc().change_mode(GcKind::Generational);
6559
6560 let root_value = LuaValue::Table(root);
6561 for i in 1..=64 {
6562 let child = state.new_table();
6563 let child_value = LuaValue::Table(child);
6564 root.raw_set_int(&mut state, i, child_value.clone())
6565 .expect("table store should succeed");
6566 state.gc_barrier_back(&root_value, &child_value);
6567 }
6568
6569 {
6570 let mut g = state.global_mut();
6571 g.gc_estimate = 1;
6572 set_debt(&mut *g, -1);
6573 g.heap.set_threshold_bytes(1);
6574 }
6575
6576 assert!(state.gc().generational_step());
6577 let g = state.global();
6578 assert!(g.is_gen_mode());
6579 assert!(
6580 g.lastatomic > 0,
6581 "implicit threshold-triggered growth should arm a bad major"
6582 );
6583 assert!(g.gc_debt() <= 0);
6584 drop(g);
6585
6586 assert!(state.external_unroot_value(root_key).is_some());
6587 state.gc().full_collect();
6588 }
6589
6590 #[test]
6591 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
6592 let mut state = new_state().expect("state should initialize");
6593 let _heap_guard = {
6594 let g = state.global();
6595 lua_gc::HeapGuard::push(&g.heap)
6596 };
6597
6598 let root = state.new_table();
6599 let root_key = state.external_root_value(LuaValue::Table(root));
6600 state.gc().change_mode(GcKind::Generational);
6601 {
6602 let mut g = state.global_mut();
6603 g.lastatomic = 1024;
6604 }
6605
6606 assert!(state.gc().generational_step());
6607 let g = state.global();
6608 assert_eq!(g.gckind, GcKind::Generational as u8);
6609 assert_eq!(g.lastatomic, 0);
6610 assert!(g.gc_debt() <= 0);
6611 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6612 assert_eq!(root.0.color(), lua_gc::Color::Black);
6613 drop(g);
6614
6615 assert!(state.external_unroot_value(root_key).is_some());
6616 state.gc().full_collect();
6617 }
6618
6619 #[test]
6620 fn generational_step_zero_reports_false_without_positive_debt() {
6621 let mut state = new_state().expect("state should initialize");
6622 let _heap_guard = {
6623 let g = state.global();
6624 lua_gc::HeapGuard::push(&g.heap)
6625 };
6626
6627 state.gc().change_mode(GcKind::Generational);
6628 assert_eq!(
6629 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
6630 0
6631 );
6632 assert_eq!(
6633 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
6634 1
6635 );
6636 }
6637}
6638
6639