1use std::cell::RefCell;
23use std::collections::hash_map::Entry;
24use std::collections::HashMap;
25use std::hash::{BuildHasherDefault, Hasher};
26use std::rc::Rc;
27
28use crate::string::StringPool;
29pub use lua_types::error::LuaError;
30pub use lua_types::{CallInfoIdx, StackIdx};
31
32pub struct StackIdxConv(pub StackIdx);
35
36#[inline(always)]
41pub fn stack_idx_to_i32(i: StackIdx) -> i32 { i.0 as i32 }
42
43impl From<u32> for StackIdxConv {
44 #[inline(always)]
45 fn from(v: u32) -> Self { StackIdxConv(StackIdx(v)) }
46}
47impl From<i32> for StackIdxConv {
48 #[inline(always)]
49 fn from(v: i32) -> Self { StackIdxConv(StackIdx(v.max(0) as u32)) }
50}
51impl From<usize> for StackIdxConv {
52 #[inline(always)]
53 fn from(v: usize) -> Self { StackIdxConv(StackIdx(v as u32)) }
54}
55impl From<StackIdx> for StackIdxConv {
56 #[inline(always)]
57 fn from(v: StackIdx) -> Self { StackIdxConv(v) }
58}
59pub use lua_types::value::{LuaTable, LuaValue, F2Imod};
60pub use lua_types::string::LuaString;
61pub use lua_types::userdata::LuaUserData;
62pub use lua_types::closure::{LuaCFnPtr, LuaClosure, LuaLClosure as LuaClosureLua, LuaCClosure as LuaClosureC};
63pub use lua_types::proto::LuaProto;
64pub use lua_types::upval::{UpVal, UpValState};
65pub use lua_types::gc::GcRef;
66
67pub struct LuaByteHasher {
68 hash: u64,
69}
70
71impl Default for LuaByteHasher {
72 fn default() -> Self {
73 Self { hash: 0xcbf2_9ce4_8422_2325 }
74 }
75}
76
77impl Hasher for LuaByteHasher {
78 #[inline]
79 fn write(&mut self, bytes: &[u8]) {
80 const PRIME: u64 = 0x0000_0100_0000_01b3;
81 for &byte in bytes {
82 self.hash ^= u64::from(byte);
83 self.hash = self.hash.wrapping_mul(PRIME);
84 }
85 }
86
87 #[inline]
88 fn write_u8(&mut self, i: u8) {
89 self.write(&[i]);
90 }
91
92 #[inline]
93 fn write_usize(&mut self, i: usize) {
94 self.write(&i.to_ne_bytes());
95 }
96
97 #[inline]
98 fn finish(&self) -> u64 {
99 self.hash
100 }
101}
102
103pub type LuaByteBuildHasher = BuildHasherDefault<LuaByteHasher>;
104pub type InternedStringMap = HashMap<Box<[u8]>, GcRef<LuaString>, LuaByteBuildHasher>;
105
106pub type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
113
114pub type LuaRustFunction = Rc<dyn Fn(&mut LuaState) -> Result<usize, LuaError>>;
115
116#[derive(Clone)]
117pub enum LuaCallable {
118 Bare(LuaCFunction),
119 Rust(LuaRustFunction),
120}
121
122impl std::fmt::Debug for LuaCallable {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 LuaCallable::Bare(_) => f.write_str("LuaCallable::Bare(..)"),
126 LuaCallable::Rust(_) => f.write_str("LuaCallable::Rust(..)"),
127 }
128 }
129}
130
131impl LuaCallable {
132 pub fn bare(f: LuaCFunction) -> Self {
133 LuaCallable::Bare(f)
134 }
135
136 pub fn rust(f: LuaRustFunction) -> Self {
137 LuaCallable::Rust(f)
138 }
139
140 pub fn as_bare(&self) -> Option<LuaCFunction> {
141 match self {
142 LuaCallable::Bare(f) => Some(*f),
143 LuaCallable::Rust(_) => None,
144 }
145 }
146
147 pub fn call(&self, state: &mut LuaState) -> Result<usize, LuaError> {
148 match self {
149 LuaCallable::Bare(f) => f(state),
150 LuaCallable::Rust(f) => f(state),
151 }
152 }
153}
154
155#[derive(Clone, Debug)]
156pub enum FinalizerObject {
157 Table(GcRef<LuaTable>),
158 UserData(GcRef<LuaUserData>),
159}
160
161impl FinalizerObject {
162 pub fn identity(&self) -> usize {
163 match self {
164 FinalizerObject::Table(t) => t.identity(),
165 FinalizerObject::UserData(u) => u.identity(),
166 }
167 }
168
169 pub fn metatable(&self) -> Option<GcRef<LuaTable>> {
170 match self {
171 FinalizerObject::Table(t) => t.metatable(),
172 FinalizerObject::UserData(u) => u.metatable(),
173 }
174 }
175
176 pub fn as_lua_value(&self) -> LuaValue {
177 match self {
178 FinalizerObject::Table(t) => LuaValue::Table(t.clone()),
179 FinalizerObject::UserData(u) => LuaValue::UserData(u.clone()),
180 }
181 }
182
183 pub fn mark(&self, marker: &mut lua_gc::Marker) {
184 match self {
185 FinalizerObject::Table(t) => marker.mark(t.0),
186 FinalizerObject::UserData(u) => marker.mark(u.0),
187 }
188 }
189
190 pub fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
191 Some(match self {
192 FinalizerObject::Table(t) => t.0.as_trace_ptr(),
193 FinalizerObject::UserData(u) => u.0.as_trace_ptr(),
194 })
195 }
196
197 pub fn age(&self) -> lua_gc::GcAge {
198 match self {
199 FinalizerObject::Table(t) => t.0.age(),
200 FinalizerObject::UserData(u) => u.0.age(),
201 }
202 }
203
204 pub fn is_finalized(&self) -> bool {
205 match self {
206 FinalizerObject::Table(t) => t.0.is_finalized(),
207 FinalizerObject::UserData(u) => u.0.is_finalized(),
208 }
209 }
210
211 pub fn set_finalized(&self, finalized: bool) {
212 match self {
213 FinalizerObject::Table(t) => t.0.set_finalized(finalized),
214 FinalizerObject::UserData(u) => u.0.set_finalized(finalized),
215 }
216 }
217}
218
219impl lua_gc::FinalizerEntry for FinalizerObject {
220 fn identity(&self) -> usize {
221 FinalizerObject::identity(self)
222 }
223
224 fn heap_ptr(&self) -> Option<std::ptr::NonNull<lua_gc::GcBox<dyn lua_gc::Trace>>> {
225 FinalizerObject::heap_ptr(self)
226 }
227
228 fn age(&self) -> lua_gc::GcAge {
229 FinalizerObject::age(self)
230 }
231
232 fn is_finalized(&self) -> bool {
233 FinalizerObject::is_finalized(self)
234 }
235
236 fn set_finalized(&self, finalized: bool) {
237 FinalizerObject::set_finalized(self, finalized);
238 }
239}
240
241#[derive(Clone, Debug)]
242pub struct WeakTableEntry {
243 table: lua_types::gc::GcWeak<LuaTable>,
244 kind: lua_gc::WeakListKind,
245}
246
247impl WeakTableEntry {
248 pub fn new(table: &GcRef<LuaTable>) -> Self {
249 let mode = table.weak_mode();
250 let weak_keys = (mode & (1 << 0)) != 0;
251 let weak_values = (mode & (1 << 1)) != 0;
252 let kind = match (weak_keys, weak_values) {
253 (true, true) => lua_gc::WeakListKind::AllWeak,
254 (true, false) => lua_gc::WeakListKind::Ephemeron,
255 (false, true) => lua_gc::WeakListKind::WeakValues,
256 (false, false) => lua_gc::WeakListKind::WeakValues,
257 };
258 Self { table: table.downgrade(), kind }
259 }
260}
261
262impl lua_gc::WeakEntry for WeakTableEntry {
263 type Strong = GcRef<LuaTable>;
264
265 fn identity(&self) -> usize {
266 self.table.identity()
267 }
268
269 fn list_kind(&self) -> lua_gc::WeakListKind {
270 self.kind
271 }
272
273 fn upgrade(&self) -> Option<Self::Strong> {
274 self.table.upgrade()
275 }
276}
277
278pub(crate) const EXTRA_STACK: usize = 5;
282
283pub(crate) const LUA_MINSTACK: usize = 20;
285
286pub(crate) const BASIC_STACK_SIZE: usize = 2 * LUA_MINSTACK;
288
289pub(crate) const LUAI_MAXCCALLS: u32 = 200;
309
310pub(crate) const CIST_C: u16 = 1 << 1;
312
313pub(crate) const CIST_OAH: u16 = 1 << 0;
315pub(crate) const CIST_FRESH: u16 = 1 << 2;
316pub(crate) const CIST_HOOKED: u16 = 1 << 3;
317pub(crate) const CIST_YPCALL: u16 = 1 << 4;
318pub(crate) const CIST_TAIL: u16 = 1 << 5;
319pub(crate) const CIST_HOOKYIELD: u16 = 1 << 6;
320pub(crate) const CIST_FIN: u16 = 1 << 7;
321pub(crate) const CIST_TRAN: u16 = 1 << 8;
322pub(crate) const CIST_RECST: u32 = 10;
323pub(crate) const CIST_LEQ: u16 = 1 << 13;
330
331const LUA_NUMTYPES: usize = 9;
333
334const GCSTPUSR: u8 = 1;
336const GCSTPGC: u8 = 2;
337
338const GCS_PAUSE: u8 = 0;
340
341const LUAI_GCPAUSE: u32 = 200;
342const LUAI_GCMUL: u32 = 100;
343const LUAI_GCSTEPSIZE: u8 = 13;
344const LUAI_GENMAJORMUL: u32 = 100;
345const LUAI_GENMINORMUL: u8 = 20;
346
347const WHITE0BIT: u8 = 0;
348
349const STRCACHE_N: usize = 53;
350const STRCACHE_M: usize = 2;
351
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub enum GcKind {
359 Incremental = 0,
360 Generational = 1,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub enum WarnMode {
372 Off,
373 On,
374 Cont,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub enum TestWarnMode {
380 Normal,
381 Allow,
382 Store,
383}
384
385pub use lua_types::status::LuaStatus;
390
391#[derive(Clone)]
398pub struct StackValue {
399 pub val: LuaValue,
400 pub tbc_delta: u16,
401}
402
403impl Default for StackValue {
404 fn default() -> Self {
405 StackValue {
406 val: LuaValue::Nil,
407 tbc_delta: 0,
408 }
409 }
410}
411
412#[derive(Clone)]
421pub struct CallInfo {
422 pub func: StackIdx,
424
425 pub top: StackIdx,
427
428 pub previous: Option<CallInfoIdx>,
430
431 pub next: Option<CallInfoIdx>,
433
434 pub u: CallInfoFrame,
435
436 pub u2: CallInfoExtra,
437
438 pub nresults: i16,
440
441 pub callstatus: u16,
443}
444
445#[derive(Clone, Copy)]
448pub enum CallInfoFrame {
449 Lua {
450 savedpc: u32,
452 trap: bool,
454 nextraargs: i32,
456 },
457 C {
458 k: Option<LuaKFunction>,
460 old_errfunc: isize,
462 ctx: isize,
464 },
465}
466
467pub type LuaKFunction = fn(&mut LuaState, status: i32, ctx: isize) -> Result<usize, LuaError>;
469
470#[derive(Default, Clone, Copy)]
474pub struct CallInfoExtra {
475 pub value: i32,
476 pub ftransfer: u16,
477 pub ntransfer: u16,
478}
479
480impl CallInfoFrame {
481 pub fn c_default() -> Self {
483 CallInfoFrame::C {
484 k: None,
485 old_errfunc: 0,
486 ctx: 0,
487 }
488 }
489
490 pub fn lua_default() -> Self {
492 CallInfoFrame::Lua {
493 savedpc: 0,
494 trap: false,
495 nextraargs: 0,
496 }
497 }
498}
499
500impl Default for CallInfo {
501 fn default() -> Self {
502 CallInfo {
503 func: StackIdx(0),
504 top: StackIdx(0),
505 previous: None,
506 next: None,
507 u: CallInfoFrame::c_default(),
508 u2: CallInfoExtra::default(),
509 nresults: 0,
510 callstatus: 0,
511 }
512 }
513}
514
515impl CallInfo {
516 pub fn is_lua(&self) -> bool { (self.callstatus & CIST_C) == 0 }
517 pub fn is_lua_code(&self) -> bool { self.is_lua() }
518 pub fn is_vararg_func(&self) -> bool { false }
525 pub fn saved_pc(&self) -> u32 {
526 if let CallInfoFrame::Lua { savedpc, .. } = self.u { savedpc } else { 0 }
527 }
528 pub fn set_saved_pc(&mut self, pc: u32) {
529 if let CallInfoFrame::Lua { ref mut savedpc, .. } = self.u { *savedpc = pc; }
530 }
531 pub fn nextra_args(&self) -> i32 {
532 if let CallInfoFrame::Lua { nextraargs, .. } = self.u { nextraargs } else { 0 }
533 }
534 pub fn transfer_ftransfer(&self) -> u16 { self.u2.ftransfer }
535 pub fn transfer_ntransfer(&self) -> u16 { self.u2.ntransfer }
536 pub fn set_trap(&mut self, t: bool) {
537 if let CallInfoFrame::Lua { ref mut trap, .. } = self.u { *trap = t; }
538 }
539 pub fn recover_status(&self) -> i32 {
542 ((self.callstatus >> CIST_RECST) & 7) as i32
543 }
544 pub fn set_recover_status<T: Into<i32>>(&mut self, status: T) {
547 let st = (status.into() & 7) as u16;
548 self.callstatus = (self.callstatus & !(7u16 << CIST_RECST)) | (st << CIST_RECST);
549 }
550 pub fn get_oah(&self) -> bool { (self.callstatus & CIST_OAH) != 0 }
551 pub fn set_oah(&mut self, allow: bool) {
554 self.callstatus = (self.callstatus & !CIST_OAH) | (if allow { CIST_OAH } else { 0 });
555 }
556 pub fn u_c_old_errfunc(&self) -> isize {
557 if let CallInfoFrame::C { old_errfunc, .. } = self.u { old_errfunc } else { 0 }
558 }
559 pub fn u_c_ctx(&self) -> isize {
560 if let CallInfoFrame::C { ctx, .. } = self.u { ctx } else { 0 }
561 }
562 pub fn u_c_k(&self) -> Option<LuaKFunction> {
563 if let CallInfoFrame::C { k, .. } = self.u { k } else { None }
564 }
565 pub fn set_u_c_k(&mut self, k: Option<LuaKFunction>) {
569 if let CallInfoFrame::C { k: ref mut slot, .. } = self.u {
570 *slot = k;
571 }
572 }
573 pub fn set_u_c_ctx(&mut self, ctx: isize) {
575 if let CallInfoFrame::C { ctx: ref mut slot, .. } = self.u {
576 *slot = ctx;
577 }
578 }
579 pub fn set_u_c_old_errfunc(&mut self, old_errfunc: isize) {
581 if let CallInfoFrame::C { old_errfunc: ref mut slot, .. } = self.u {
582 *slot = old_errfunc;
583 }
584 }
585 pub fn set_u2_funcidx(&mut self, idx: i32) {
588 self.u2.value = idx;
589 }
590}
591
592pub trait LuaValueExt {
598 fn base_type(&self) -> lua_types::LuaType;
599 fn to_number_no_strconv(&self) -> Option<f64>;
600 fn to_number_with_strconv(&self) -> Option<f64>;
601 fn to_integer_no_strconv(&self) -> Option<i64>;
602 fn to_integer_with_strconv(&self) -> Option<i64>;
603 fn full_type_tag(&self) -> u8;
604}
605
606impl LuaValueExt for LuaValue {
607 fn base_type(&self) -> lua_types::LuaType { self.type_tag() }
608 fn to_number_no_strconv(&self) -> Option<f64> {
609 match self {
610 LuaValue::Float(f) => Some(*f),
611 LuaValue::Int(i) => Some(*i as f64),
612 _ => None,
613 }
614 }
615 fn to_number_with_strconv(&self) -> Option<f64> {
616 if let Some(n) = self.to_number_no_strconv() { return Some(n); }
617 if let LuaValue::Str(s) = self {
618 let mut tmp = LuaValue::Nil;
619 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
620 if sz == 0 { return None; }
621 return match tmp {
622 LuaValue::Int(i) => Some(i as f64),
623 LuaValue::Float(f) => Some(f),
624 _ => None,
625 };
626 }
627 None
628 }
629 fn to_integer_no_strconv(&self) -> Option<i64> {
630 match self {
631 LuaValue::Int(i) => Some(*i),
632 LuaValue::Float(f) if f.fract() == 0.0 && f.is_finite() => {
633 let min_f = i64::MIN as f64;
637 let max_plus1_f = -(i64::MIN as f64);
638 if *f >= min_f && *f < max_plus1_f {
639 Some(*f as i64)
640 } else {
641 None
642 }
643 }
644 _ => None,
645 }
646 }
647 fn to_integer_with_strconv(&self) -> Option<i64> {
648 if let Some(i) = self.to_integer_no_strconv() { return Some(i); }
649 if let LuaValue::Str(s) = self {
650 let mut tmp = LuaValue::Nil;
651 let sz = crate::object::str2num(s.as_bytes(), &mut tmp);
652 if sz == 0 { return None; }
653 return tmp.to_integer_no_strconv();
654 }
655 None
656 }
657 fn full_type_tag(&self) -> u8 {
658 match self {
659 LuaValue::Nil => 0x00,
660 LuaValue::Bool(false) => 0x01,
661 LuaValue::Bool(true) => 0x11,
662 LuaValue::Int(_) => 0x03,
663 LuaValue::Float(_) => 0x13,
664 LuaValue::Str(s) if s.is_short() => 0x04,
665 LuaValue::Str(_) => 0x14,
666 LuaValue::LightUserData(_) => 0x02,
667 LuaValue::Table(_) => 0x05,
668 LuaValue::Function(LuaClosure::Lua(_)) => 0x06,
669 LuaValue::Function(LuaClosure::LightC(_)) => 0x16,
670 LuaValue::Function(LuaClosure::C(_)) => 0x26,
671 LuaValue::UserData(_) => 0x07,
672 LuaValue::Thread(_) => 0x08,
673 }
674 }
675}
676
677pub trait LuaTypeExt {
679 fn type_name(&self) -> &'static [u8];
680}
681
682impl LuaTypeExt for lua_types::LuaType {
683 fn type_name(&self) -> &'static [u8] {
684 use lua_types::LuaType::*;
685 match self {
686 None => b"no value",
687 Nil => b"nil",
688 Boolean => b"boolean",
689 LightUserData => b"userdata",
690 Number => b"number",
691 String => b"string",
692 Table => b"table",
693 Function => b"function",
694 UserData => b"userdata",
695 Thread => b"thread",
696 }
697 }
698}
699
700pub trait StackIdxExt {
704 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32;
705 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32;
706 fn raw(self) -> u32;
707}
708impl StackIdxExt for StackIdx {
709 #[inline(always)]
710 fn saturating_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.saturating_sub(n.into().0.0) }
711 #[inline(always)]
712 fn wrapping_sub(self, n: impl Into<StackIdxConv>) -> u32 { self.0.wrapping_sub(n.into().0.0) }
713 #[inline(always)]
714 fn raw(self) -> u32 { self.0 }
715}
716
717pub trait LuaTableRefExt {
727 fn metatable(&self) -> Option<GcRef<LuaTable>>;
728 fn as_ptr(&self) -> *const ();
729 fn get(&self, _k: &LuaValue) -> LuaValue;
730 fn get_int(&self, _k: i64) -> LuaValue;
731 fn get_short_str(&self, _k: &GcRef<LuaString>) -> LuaValue;
732 fn raw_set(&self, _state: &mut LuaState, _k: LuaValue, _v: LuaValue) -> Result<(), LuaError>;
733 fn raw_set_int(&self, _state: &mut LuaState, _k: i64, _v: LuaValue) -> Result<(), LuaError>;
734 fn invalidate_tm_cache(&self);
735 fn resize(&self, _state: &mut LuaState, _na: usize, _nh: usize) -> Result<(), LuaError>;
736 fn next(&self, _k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError>;
737}
738impl LuaTableRefExt for GcRef<LuaTable> {
739 #[inline]
740 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
741 #[inline]
742 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
743 #[inline]
744 fn get(&self, k: &LuaValue) -> LuaValue { (**self).get(k) }
745 #[inline]
746 fn get_int(&self, k: i64) -> LuaValue { (**self).get_int(k) }
747 #[inline]
748 fn get_short_str(&self, k: &GcRef<LuaString>) -> LuaValue { (**self).get_short_str(k) }
749 #[inline]
752 fn raw_set(&self, _state: &mut LuaState, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
753 let before = (**self).buffer_bytes();
754 let result = (**self).try_raw_set(k, v);
755 if result.is_ok() {
756 account_table_buffer_delta(self, before);
757 }
758 result
759 }
760 #[inline]
761 fn raw_set_int(&self, _state: &mut LuaState, k: i64, v: LuaValue) -> Result<(), LuaError> {
762 let before = (**self).buffer_bytes();
763 let result = (**self).try_raw_set_int(k, v);
764 if result.is_ok() {
765 account_table_buffer_delta(self, before);
766 }
767 result
768 }
769 fn invalidate_tm_cache(&self) {}
770 fn resize(&self, _state: &mut LuaState, na: usize, nh: usize) -> Result<(), LuaError> {
771 let before = (**self).buffer_bytes();
772 let na32 = na.min(u32::MAX as usize) as u32;
773 let nh32 = nh.min(u32::MAX as usize) as u32;
774 let result = (**self).resize(na32, nh32);
775 if result.is_ok() {
776 account_table_buffer_delta(self, before);
777 }
778 result
779 }
780 fn next(&self, k: LuaValue) -> Result<Option<(LuaValue, LuaValue)>, LuaError> {
781 (**self).try_next_pair(&k)
782 }
783}
784
785#[inline]
786fn account_table_buffer_delta(t: &GcRef<LuaTable>, before: usize) {
787 let after = (**t).buffer_bytes();
788 if after > before {
789 t.account_buffer((after - before) as isize);
790 } else if before > after {
791 t.account_buffer(-((before - after) as isize));
792 }
793}
794
795pub trait LuaUserDataRefExt {
796 fn metatable(&self) -> Option<GcRef<LuaTable>>;
797 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>);
798 fn as_ptr(&self) -> *const ();
799 fn len(&self) -> usize;
800}
801impl LuaUserDataRefExt for GcRef<LuaUserData> {
802 fn metatable(&self) -> Option<GcRef<LuaTable>> { (**self).metatable() }
803 fn set_metatable(&self, mt: Option<GcRef<LuaTable>>) { (**self).set_metatable(mt); }
804 fn as_ptr(&self) -> *const () { GcRef::identity(self) as *const () }
805 fn len(&self) -> usize { self.0.data.len() }
806}
807
808pub trait LuaStringRefExt {
809 fn is_white(&self) -> bool;
810 fn hash(&self) -> u32;
811 fn as_gc_ref(&self) -> GcRef<LuaString>;
812}
813impl LuaStringRefExt for GcRef<LuaString> {
814 fn is_white(&self) -> bool { false }
815 fn hash(&self) -> u32 { self.0.hash() }
816 fn as_gc_ref(&self) -> GcRef<LuaString> { self.clone() }
817}
818
819pub trait LuaLClosureRefExt {
820 fn proto(&self) -> &GcRef<LuaProto>;
821 fn nupvalues(&self) -> usize;
822}
823impl LuaLClosureRefExt for GcRef<lua_types::closure::LuaLClosure> {
824 fn proto(&self) -> &GcRef<LuaProto> { &self.0.proto }
825 fn nupvalues(&self) -> usize { self.0.upvals.len() }
826}
827
828pub trait LuaClosureExt {
830 fn nupvalues(&self) -> usize;
831}
832impl LuaClosureExt for LuaClosure {
833 fn nupvalues(&self) -> usize {
834 match self {
835 LuaClosure::Lua(l) => l.0.upvals.len(),
836 LuaClosure::C(c) => c.0.upvalues.borrow().len(),
837 LuaClosure::LightC(_) => 0,
838 }
839 }
840}
841
842pub trait LuaProtoExt {
844 fn source_bytes(&self) -> &[u8];
845 fn source_string(&self) -> Option<&GcRef<LuaString>>;
846}
847impl LuaProtoExt for LuaProto {
848 fn source_bytes(&self) -> &[u8] {
849 match &self.source { Some(s) => s.0.as_bytes(), None => &[] }
850 }
851 fn source_string(&self) -> Option<&GcRef<LuaString>> { self.source.as_ref() }
852}
853
854pub trait Collectable: std::fmt::Debug {}
861
862impl std::fmt::Debug for LuaState {
863 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
864 write!(f, "LuaState")
865 }
866}
867impl Collectable for LuaState {}
868
869pub type ParserHook = fn(
878 state: &mut LuaState,
879 source: &[u8],
880 name: &[u8],
881 firstchar: i32,
882) -> Result<GcRef<lua_types::closure::LuaLClosure>, LuaError>;
883
884pub type FileLoaderHook = fn(filename: &[u8]) -> Result<Vec<u8>, LuaError>;
892
893pub type FileOpenHook =
904 fn(filename: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
905
906pub type OutputHook = fn(bytes: &[u8]) -> std::io::Result<()>;
914
915pub type InputHook = fn(buf: &mut [u8]) -> std::io::Result<usize>;
918
919pub type EnvHook = fn(name: &[u8]) -> Option<Vec<u8>>;
925
926pub type UnixTimeHook = fn() -> i64;
928
929pub type CpuClockHook = fn() -> f64;
937
938pub type LocalOffsetHook = fn(timestamp: i64) -> i64;
950
951pub type EntropyHook = fn() -> u64;
955
956pub type TempNameHook = fn() -> Result<Vec<u8>, LuaError>;
961
962pub type PopenHook =
973 fn(cmd: &[u8], mode: &[u8]) -> Result<Box<dyn lua_types::LuaFileHandle>, LuaError>;
974
975pub type FileRemoveHook = fn(filename: &[u8]) -> Result<(), LuaError>;
981
982pub type FileRenameHook = fn(from: &[u8], to: &[u8]) -> Result<(), LuaError>;
988
989#[derive(Clone, Copy, Debug)]
995pub enum OsExecuteReason {
996 Exit,
998 Signal,
1000}
1001
1002#[derive(Debug)]
1005pub struct OsExecuteResult {
1006 pub success: bool,
1008 pub reason: OsExecuteReason,
1010 pub code: i32,
1012}
1013
1014pub type OsExecuteHook = fn(cmd: &[u8]) -> Result<OsExecuteResult, LuaError>;
1021
1022#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1029pub struct DynLibId(pub u64);
1030
1031pub enum DynamicSymbol {
1039 RustNative(LuaCFunction),
1042 LuaCAbi(*const ()),
1048 Unsupported { reason: Vec<u8> },
1051}
1052
1053pub type DynLibLoadHook =
1064 fn(state: &mut LuaState, path: &[u8], see_global: bool) -> Result<DynLibId, LuaError>;
1065
1066pub type DynLibSymbolHook =
1074 fn(state: &mut LuaState, handle: DynLibId, symbol: &[u8]) -> Result<DynamicSymbol, LuaError>;
1075
1076pub type DynLibUnloadHook = fn(handle: DynLibId);
1084
1085pub struct ThreadRegistryEntry {
1091 pub state: Rc<RefCell<LuaState>>,
1096 pub value: GcRef<lua_types::value::LuaThread>,
1099}
1100
1101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1106pub struct ExternalRootKey {
1107 index: usize,
1108 generation: u64,
1109}
1110
1111#[derive(Debug)]
1112struct ExternalRootSlot {
1113 value: Option<LuaValue>,
1114 generation: u64,
1115}
1116
1117#[derive(Debug, Default)]
1123pub struct ExternalRootSet {
1124 slots: Vec<ExternalRootSlot>,
1125 free: Vec<usize>,
1126 live: usize,
1127}
1128
1129impl ExternalRootSet {
1130 pub fn insert(&mut self, value: LuaValue) -> ExternalRootKey {
1131 if let Some(index) = self.free.pop() {
1132 let slot = &mut self.slots[index];
1133 debug_assert!(
1134 slot.value.is_none(),
1135 "free external-root slot is occupied"
1136 );
1137 slot.generation = slot.generation.wrapping_add(1).max(1);
1138 slot.value = Some(value);
1139 self.live += 1;
1140 ExternalRootKey {
1141 index,
1142 generation: slot.generation,
1143 }
1144 } else {
1145 let index = self.slots.len();
1146 self.slots.push(ExternalRootSlot {
1147 value: Some(value),
1148 generation: 1,
1149 });
1150 self.live += 1;
1151 ExternalRootKey {
1152 index,
1153 generation: 1,
1154 }
1155 }
1156 }
1157
1158 pub fn get(&self, key: ExternalRootKey) -> Option<&LuaValue> {
1159 let slot = self.slots.get(key.index)?;
1160 if slot.generation == key.generation {
1161 slot.value.as_ref()
1162 } else {
1163 None
1164 }
1165 }
1166
1167 pub fn replace(&mut self, key: ExternalRootKey, value: LuaValue) -> Option<LuaValue> {
1168 let slot = self.slots.get_mut(key.index)?;
1169 if slot.generation != key.generation || slot.value.is_none() {
1170 return None;
1171 }
1172 slot.value.replace(value)
1173 }
1174
1175 pub fn remove(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
1176 let slot = self.slots.get_mut(key.index)?;
1177 if slot.generation != key.generation {
1178 return None;
1179 }
1180 let old = slot.value.take()?;
1181 self.free.push(key.index);
1182 self.live -= 1;
1183 Some(old)
1184 }
1185
1186 pub fn iter_values(&self) -> impl Iterator<Item = &LuaValue> {
1187 self.slots.iter().filter_map(|slot| slot.value.as_ref())
1188 }
1189
1190 pub fn len(&self) -> usize {
1191 self.live
1192 }
1193
1194 pub fn is_empty(&self) -> bool {
1195 self.live == 0
1196 }
1197
1198 pub fn vacant_len(&self) -> usize {
1199 self.free.len()
1200 }
1201}
1202
1203pub struct GlobalState {
1209 pub parser_hook: Option<ParserHook>,
1215
1216 pub cli_argv: Option<Vec<Vec<u8>>>,
1223
1224 pub cli_preload: Option<fn(&mut LuaState) -> Result<(), LuaError>>,
1228
1229 pub lua_version: lua_types::LuaVersion,
1236
1237 pub file_loader_hook: Option<FileLoaderHook>,
1242
1243 pub file_open_hook: Option<FileOpenHook>,
1248
1249 pub stdout_hook: Option<OutputHook>,
1253
1254 pub stderr_hook: Option<OutputHook>,
1256
1257 pub stdin_hook: Option<InputHook>,
1260
1261 pub env_hook: Option<EnvHook>,
1263
1264 pub unix_time_hook: Option<UnixTimeHook>,
1267
1268 pub cpu_clock_hook: Option<CpuClockHook>,
1271
1272 pub local_offset_hook: Option<LocalOffsetHook>,
1277
1278 pub entropy_hook: Option<EntropyHook>,
1281
1282 pub temp_name_hook: Option<TempNameHook>,
1284
1285 pub popen_hook: Option<PopenHook>,
1290
1291 pub file_remove_hook: Option<FileRemoveHook>,
1294
1295 pub file_rename_hook: Option<FileRenameHook>,
1298
1299 pub os_execute_hook: Option<OsExecuteHook>,
1303
1304 pub dynlib_load_hook: Option<DynLibLoadHook>,
1309
1310 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
1314
1315 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
1319
1320 pub sandbox: SandboxLimits,
1323
1324 pub gc_debt: isize,
1326
1327 pub gc_estimate: usize,
1328
1329 pub lastatomic: usize,
1331
1332 pub strt: StringPool,
1334
1335 pub l_registry: LuaValue,
1337
1338 pub external_roots: ExternalRootSet,
1341
1342 pub globals: LuaValue,
1349 pub loaded: LuaValue,
1350
1351 pub nilvalue: LuaValue,
1355
1356 pub seed: u32,
1358
1359 pub currentwhite: u8,
1361
1362 pub gcstate: u8,
1363
1364 pub gckind: u8,
1365
1366 pub gcstopem: bool,
1367
1368 pub genminormul: u8,
1370
1371 pub genmajormul: u8,
1372
1373 pub gcstp: u8,
1374
1375 pub gcemergency: bool,
1376
1377 pub gcpause: u8,
1379
1380 pub gcstepmul: u8,
1382
1383 pub gcstepsize: u8,
1384
1385 pub gc55_params: [i64; 6],
1394
1395 pub sweepgc_cursor: usize,
1400
1401 pub weak_tables_registry: lua_gc::WeakRegistry<WeakTableEntry>,
1410
1411 pub finalizers: lua_gc::FinalizerRegistry<FinalizerObject>,
1414
1415 pub gc_finalizer_error: Option<LuaValue>,
1426
1427 pub twups: Vec<GcRef<LuaState>>,
1437
1438 pub panic: Option<LuaCFunction>,
1440
1441 pub mainthread: Option<GcRef<LuaState>>,
1444
1445 pub threads: std::collections::HashMap<u64, ThreadRegistryEntry>,
1458
1459 pub main_thread_value: GcRef<lua_types::value::LuaThread>,
1464
1465 pub current_thread_id: u64,
1470
1471 pub main_thread_id: u64,
1474
1475 pub next_thread_id: u64,
1478
1479 pub memerrmsg: GcRef<LuaString>,
1481
1482 pub tmname: Vec<GcRef<LuaString>>,
1485
1486 pub mt: [Option<GcRef<LuaTable>>; LUA_NUMTYPES],
1488
1489 pub strcache: [[GcRef<LuaString>; STRCACHE_M]; STRCACHE_N],
1491
1492 pub interned_lt: InternedStringMap,
1498
1499 pub warnf: Option<Box<dyn FnMut(&[u8], bool)>>,
1501
1502 pub warn_mode: WarnMode,
1507
1508 pub test_warn_enabled: bool,
1513 pub test_warn_on: bool,
1514 pub test_warn_mode: TestWarnMode,
1515 pub test_warn_last_to_cont: bool,
1516 pub test_warn_buffer: Vec<u8>,
1517
1518 pub c_functions: Vec<LuaCallable>,
1523
1524 pub heap: lua_gc::Heap,
1529
1530 pub cross_thread_upvals: std::collections::HashMap<(u64, StackIdx), LuaValue>,
1544
1545 pub suspended_parent_stacks: Vec<Vec<LuaValue>>,
1559
1560 pub suspended_parent_open_upvals: Vec<Vec<GcRef<UpVal>>>,
1566}
1567
1568const SANDBOX_COUNT_MASK: u8 = 1 << 3;
1571
1572pub const SANDBOX_TRIP_NONE: u8 = 0;
1574pub const SANDBOX_TRIP_INSTRUCTIONS: u8 = 1;
1576pub const SANDBOX_TRIP_MEMORY: u8 = 2;
1578
1579#[derive(Default)]
1586pub struct SandboxLimits {
1587 pub interval: std::cell::Cell<i32>,
1589 pub instr_limited: std::cell::Cell<bool>,
1591 pub instr_remaining: std::cell::Cell<u64>,
1593 pub instr_limit: std::cell::Cell<u64>,
1595 pub mem_limit: std::cell::Cell<Option<usize>>,
1597 pub tripped: std::cell::Cell<u8>,
1599 pub aborting: std::cell::Cell<bool>,
1604}
1605
1606impl GlobalState {
1607 pub fn sandbox_active(&self) -> bool {
1609 self.sandbox.interval.get() != 0
1610 }
1611
1612 pub fn total_bytes(&self) -> usize {
1617 self.heap.bytes_used().max(1)
1618 }
1619
1620 pub fn get_thread(&self, id: u64) -> Option<&ThreadRegistryEntry> {
1625 self.threads.get(&id)
1626 }
1627
1628 pub fn thread_value_for(&self, id: u64) -> Option<GcRef<lua_types::value::LuaThread>> {
1632 if id == self.main_thread_id {
1633 Some(self.main_thread_value.clone())
1634 } else {
1635 self.threads.get(&id).map(|e| e.value.clone())
1636 }
1637 }
1638
1639 pub fn is_complete(&self) -> bool {
1646 matches!(self.nilvalue, LuaValue::Nil)
1647 }
1648
1649 pub fn current_white(&self) -> u8 {
1657 self.currentwhite
1658 }
1659
1660 pub fn other_white(&self) -> u8 {
1664 self.currentwhite ^ 0x03
1665 }
1666
1667 pub fn is_gen_mode(&self) -> bool {
1671 self.gckind == GcKind::Generational as u8 || self.lastatomic != 0
1672 }
1673
1674 pub fn gc_running(&self) -> bool {
1678 self.gcstp == 0
1679 }
1680
1681 pub fn keep_invariant(&self) -> bool {
1685 self.heap.gc_state().is_invariant()
1686 }
1687
1688 pub fn is_sweep_phase(&self) -> bool {
1692 self.heap.gc_state().is_sweep()
1693 }
1694
1695 pub fn gc_debt(&self) -> isize { self.gc_debt }
1697 pub fn set_gc_debt(&mut self, d: isize) { self.gc_debt = d; }
1698 pub fn gc_at_pause(&self) -> bool { self.heap.gc_state().is_pause() }
1699 fn get_gc_param(p: u8) -> i32 { (p as i32) * 4 }
1700 fn set_gc_param_slot(slot: &mut u8, p: i32) { *slot = (p / 4) as u8; }
1701 pub fn gc_pause_param(&self) -> i32 { Self::get_gc_param(self.gcpause) }
1702 pub fn set_gc_pause_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcpause, p); }
1703 pub fn gc_stepmul_param(&self) -> i32 { Self::get_gc_param(self.gcstepmul) }
1704 pub fn set_gc_stepmul_param(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.gcstepmul, p); }
1705 pub fn gc_genmajormul_param(&self) -> i32 { Self::get_gc_param(self.genmajormul) }
1706 pub fn set_gc_genmajormul(&mut self, p: i32) { Self::set_gc_param_slot(&mut self.genmajormul, p); }
1707 pub fn gc55_param(&mut self, idx: usize, value: i64) -> i64 {
1711 let old = self.gc55_params[idx];
1712 if value >= 0 {
1713 self.gc55_params[idx] = value;
1714 }
1715 old
1716 }
1717 pub fn gc_stop_flags(&self) -> u8 { self.gcstp }
1718 pub fn set_gc_stop_flags(&mut self, f: u8) { self.gcstp = f; }
1719 pub fn stop_gc_internal(&mut self) -> u8 {
1720 let old = self.gcstp;
1721 self.gcstp |= GCSTPGC;
1722 old
1723 }
1724 pub fn set_gc_stop_user(&mut self) {
1725 self.gcstp = GCSTPUSR;
1727 }
1728 pub fn clear_gc_stop(&mut self) { self.gcstp = 0; }
1729 pub fn is_gc_running(&self) -> bool { self.gcstp == 0 }
1730 pub fn is_gc_stopped_internally(&self) -> bool { (self.gcstp & GCSTPGC) != 0 }
1735
1736 pub fn tm_name<T: TmIndex>(&self, tm: T) -> Option<GcRef<LuaString>> {
1746 self.tmname.get(tm.tm_index()).cloned()
1747 }
1748}
1749
1750pub trait TmIndex: Copy {
1756 fn tm_index(self) -> usize;
1757}
1758impl TmIndex for lua_types::tagmethod::TagMethod {
1759 fn tm_index(self) -> usize { self as u8 as usize }
1760}
1761impl TmIndex for crate::tagmethods::TagMethod {
1762 fn tm_index(self) -> usize { self as u8 as usize }
1763}
1764impl TmIndex for usize {
1765 fn tm_index(self) -> usize { self }
1766}
1767impl TmIndex for u8 {
1768 fn tm_index(self) -> usize { self as usize }
1769}
1770
1771use lua_types::tagmethod::TagMethod;
1772
1773pub struct LuaState {
1783 pub status: u8,
1787
1788 pub allowhook: bool,
1790
1791 pub nci: u32,
1793
1794 pub top: StackIdx,
1798
1799 pub stack_last: StackIdx,
1801
1802 pub stack: Vec<StackValue>,
1804
1805 pub ci: CallInfoIdx,
1809
1810 pub call_info: Vec<CallInfo>,
1813
1814 pub openupval: Vec<GcRef<UpVal>>,
1818
1819 pub tbclist: Vec<StackIdx>,
1821
1822 pub(crate) global: Rc<RefCell<GlobalState>>,
1827
1828 pub hook: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>,
1832
1833 pub hookmask: u8,
1835
1836 pub basehookcount: i32,
1838
1839 pub hookcount: i32,
1841
1842 pub errfunc: isize,
1849
1850 pub n_ccalls: u32,
1854
1855 pub oldpc: u32,
1859
1860 pub marked: u8,
1864
1865 pub cached_thread_id: u64,
1881
1882 pub gc_check_needed: bool,
1887
1888}
1889
1890impl LuaState {
1891 pub fn global(&self) -> std::cell::Ref<'_, GlobalState> {
1899 self.global.borrow()
1900 }
1901
1902 pub fn global_mut(&self) -> std::cell::RefMut<'_, GlobalState> {
1906 self.global.borrow_mut()
1907 }
1908
1909 pub fn global_rc(&self) -> Rc<RefCell<GlobalState>> {
1913 Rc::clone(&self.global)
1914 }
1915
1916 pub fn enable_test_warning_handler(&mut self) -> Result<(), LuaError> {
1918 {
1919 let mut g = self.global_mut();
1920 g.test_warn_enabled = true;
1921 g.test_warn_on = false;
1922 g.test_warn_mode = TestWarnMode::Normal;
1923 g.test_warn_last_to_cont = false;
1924 g.test_warn_buffer.clear();
1925 }
1926 self.push(LuaValue::Bool(false));
1927 crate::api::set_global(self, b"_WARN")
1928 }
1929
1930 pub fn c_calls(&self) -> u32 {
1934 self.n_ccalls & 0xffff
1935 }
1936
1937 pub fn inc_nny(&mut self) {
1941 self.n_ccalls += 0x10000;
1942 }
1943
1944 pub fn dec_nny(&mut self) {
1948 self.n_ccalls -= 0x10000;
1949 }
1950
1951 pub fn is_yieldable(&self) -> bool {
1955 (self.n_ccalls & 0xffff0000) == 0
1956 }
1957
1958 pub fn reset_hook_count(&mut self) {
1962 self.hookcount = self.basehookcount;
1963 }
1964
1965 pub fn install_sandbox_limits(
1974 &mut self,
1975 interval: i32,
1976 instr_limit: Option<u64>,
1977 mem_limit: Option<usize>,
1978 ) {
1979 let interval = interval.max(1);
1980 {
1981 let g = self.global();
1982 g.sandbox.interval.set(interval);
1983 g.sandbox.instr_limited.set(instr_limit.is_some());
1984 g.sandbox.instr_remaining.set(instr_limit.unwrap_or(0));
1985 g.sandbox.instr_limit.set(instr_limit.unwrap_or(0));
1986 g.sandbox.mem_limit.set(mem_limit);
1987 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
1988 }
1989 self.hookmask |= SANDBOX_COUNT_MASK;
1990 self.basehookcount = interval;
1991 self.hookcount = interval;
1992 crate::debug::arm_traps(self);
1993 }
1994
1995 pub fn sandbox_charge_interval(&self) -> Option<LuaError> {
2000 let interval = self.global().sandbox.interval.get();
2001 self.sandbox_charge(interval as u64)
2002 }
2003
2004 pub fn sandbox_charge(&self, amount: u64) -> Option<LuaError> {
2013 let g = self.global();
2014 if g.sandbox.interval.get() == 0 {
2015 return None;
2016 }
2017 if g.sandbox.instr_limited.get() {
2018 let rem = g.sandbox.instr_remaining.get().saturating_sub(amount);
2019 g.sandbox.instr_remaining.set(rem);
2020 if rem == 0 {
2021 g.sandbox.tripped.set(SANDBOX_TRIP_INSTRUCTIONS);
2022 g.sandbox.aborting.set(true);
2023 return Some(LuaError::runtime(format_args!(
2024 "sandbox: instruction budget exhausted"
2025 )));
2026 }
2027 }
2028 if let Some(limit) = g.sandbox.mem_limit.get() {
2029 if g.total_bytes() > limit {
2030 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2031 g.sandbox.aborting.set(true);
2032 return Some(LuaError::runtime(format_args!(
2033 "sandbox: memory limit exceeded"
2034 )));
2035 }
2036 }
2037 None
2038 }
2039
2040 pub fn sandbox_reserve(&self, additional: usize) -> Option<LuaError> {
2048 let g = self.global();
2049 if g.sandbox.interval.get() == 0 {
2050 return None;
2051 }
2052 if let Some(limit) = g.sandbox.mem_limit.get() {
2053 let projected = g.total_bytes().saturating_add(additional);
2054 if projected > limit {
2055 g.sandbox.tripped.set(SANDBOX_TRIP_MEMORY);
2056 g.sandbox.aborting.set(true);
2057 return Some(LuaError::runtime(format_args!(
2058 "sandbox: memory limit exceeded"
2059 )));
2060 }
2061 }
2062 None
2063 }
2064
2065 pub fn sandbox_match_step_limit(&self) -> u64 {
2070 let g = self.global();
2071 if g.sandbox.interval.get() != 0 && g.sandbox.instr_limited.get() {
2072 g.sandbox.instr_remaining.get()
2073 } else {
2074 0
2075 }
2076 }
2077
2078 pub fn sandbox_aborting(&self) -> bool {
2082 self.global().sandbox.aborting.get()
2083 }
2084
2085 pub fn sandbox_instr_limited(&self) -> bool {
2087 self.global().sandbox.instr_limited.get()
2088 }
2089
2090 pub fn sandbox_instr_remaining(&self) -> u64 {
2093 self.global().sandbox.instr_remaining.get()
2094 }
2095
2096 pub fn sandbox_instr_limit(&self) -> u64 {
2098 self.global().sandbox.instr_limit.get()
2099 }
2100
2101 pub fn sandbox_tripped_code(&self) -> u8 {
2103 self.global().sandbox.tripped.get()
2104 }
2105
2106 pub fn sandbox_reset(&self) {
2109 let g = self.global();
2110 if g.sandbox.instr_limited.get() {
2111 g.sandbox.instr_remaining.set(g.sandbox.instr_limit.get());
2112 }
2113 g.sandbox.tripped.set(SANDBOX_TRIP_NONE);
2114 g.sandbox.aborting.set(false);
2115 }
2116
2117 pub fn stack_size(&self) -> usize {
2121 self.stack_last.0 as usize
2122 }
2123
2124 #[inline(always)]
2128 pub fn push(&mut self, val: LuaValue) {
2129 let top = self.top.0 as usize;
2130 if top < self.stack.len() {
2131 self.stack[top] = StackValue { val, tbc_delta: 0 };
2132 } else {
2133 self.stack.push(StackValue { val, tbc_delta: 0 });
2134 }
2135 self.top = StackIdx(self.top.0 + 1);
2136 }
2137
2138 #[inline(always)]
2141 pub fn pop(&mut self) -> LuaValue {
2142 if self.top.0 == 0 {
2143 return LuaValue::Nil;
2144 }
2145 self.top = StackIdx(self.top.0 - 1);
2146 self.stack[self.top.0 as usize].val.clone()
2147 }
2148
2149 #[inline(always)]
2153 pub fn stack_val(&self, idx: StackIdx) -> &LuaValue {
2154 &self.stack[idx.0 as usize].val
2155 }
2156
2157 #[inline(always)]
2159 pub fn set_stack_val(&mut self, idx: StackIdx, val: LuaValue) {
2160 self.stack[idx.0 as usize].val = val;
2161 }
2162
2163 pub fn gc(&mut self) -> GcHandle<'_> {
2170 GcHandle { _state: self }
2171 }
2172
2173 pub fn external_root_value(&mut self, value: LuaValue) -> ExternalRootKey {
2175 self.global_mut().external_roots.insert(value)
2176 }
2177
2178 pub fn external_rooted_value(&self, key: ExternalRootKey) -> Option<LuaValue> {
2180 self.global().external_roots.get(key).cloned()
2181 }
2182
2183 pub fn external_replace_root(
2185 &mut self,
2186 key: ExternalRootKey,
2187 value: LuaValue,
2188 ) -> Option<LuaValue> {
2189 self.global_mut().external_roots.replace(key, value)
2190 }
2191
2192 pub fn external_unroot_value(&mut self, key: ExternalRootKey) -> Option<LuaValue> {
2194 self.global_mut().external_roots.remove(key)
2195 }
2196
2197 pub fn try_external_unroot_value(
2200 &mut self,
2201 key: ExternalRootKey,
2202 ) -> std::result::Result<Option<LuaValue>, std::cell::BorrowMutError> {
2203 self.global
2204 .try_borrow_mut()
2205 .map(|mut global| global.external_roots.remove(key))
2206 }
2207
2208 pub fn new_table(&mut self) -> GcRef<LuaTable> {
2212 self.mark_gc_check_needed();
2214 GcRef::new(LuaTable::placeholder())
2215 }
2216
2217 pub fn new_table_with_sizes(
2222 &mut self,
2223 array_size: u32,
2224 hash_size: u32,
2225 ) -> Result<GcRef<LuaTable>, LuaError> {
2226 self.mark_gc_check_needed();
2227 let t = GcRef::new(LuaTable::placeholder());
2228 self.table_resize(&t, array_size as usize, hash_size as usize)?;
2229 Ok(t)
2230 }
2231
2232 pub fn intern_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2243 if bytes.len() <= crate::string::MAX_SHORT_LEN {
2244 let mut inserted = false;
2245 let interned = {
2246 let key = bytes.to_vec().into_boxed_slice();
2247 let mut global = self.global_mut();
2248 match global.interned_lt.entry(key) {
2249 Entry::Occupied(existing) => existing.get().clone(),
2250 Entry::Vacant(vacant) => {
2251 let new_ref = GcRef::new(LuaString::from_bytes(vacant.key().to_vec()));
2252 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2253 vacant.insert(new_ref.clone());
2254 inserted = true;
2255 new_ref
2256 }
2257 }
2258 };
2259 if inserted {
2260 self.mark_gc_check_needed();
2261 }
2262 Ok(interned)
2263 } else {
2264 self.mark_gc_check_needed();
2265 let new_ref = GcRef::new(LuaString::from_bytes(bytes.to_vec()));
2266 new_ref.account_buffer(new_ref.buffer_bytes() as isize);
2267 Ok(new_ref)
2268 }
2269 }
2270
2271 #[inline(always)]
2273 pub fn top_idx(&self) -> StackIdx {
2274 self.top
2275 }
2276}
2277
2278impl LuaState {
2287 #[inline(always)]
2288 pub fn get_at(&self, idx: impl Into<StackIdxConv>) -> LuaValue {
2289 let i: StackIdx = idx.into().0;
2290 match self.stack.get(i.0 as usize) {
2291 Some(slot) => slot.val.clone(),
2292 None => LuaValue::Nil,
2293 }
2294 }
2295 #[inline(always)]
2296 pub fn set_at(&mut self, idx: impl Into<StackIdxConv>, v: LuaValue) {
2297 let i: StackIdx = idx.into().0;
2298 self.stack[i.0 as usize].val = v;
2299 }
2300
2301 pub fn clear_stack_range(&mut self, start: StackIdx, end: StackIdx) {
2307 if end.0 <= start.0 {
2308 return;
2309 }
2310 let end_u = end.0 as usize;
2311 if end_u > self.stack.len() {
2312 self.stack.resize_with(end_u, StackValue::default);
2313 }
2314 for i in start.0..end.0 {
2315 self.stack[i as usize].val = LuaValue::Nil;
2316 self.stack[i as usize].tbc_delta = 0;
2317 }
2318 }
2319 #[inline(always)]
2327 pub fn get_int_at(&self, idx: impl Into<StackIdxConv>) -> Option<i64> {
2328 let i: StackIdx = idx.into().0;
2329 match self.stack.get(i.0 as usize) {
2330 Some(slot) => match &slot.val {
2331 LuaValue::Int(v) => Some(*v),
2332 _ => None,
2333 },
2334 None => None,
2335 }
2336 }
2337 #[inline(always)]
2344 pub fn get_int_pair_at(
2345 &self,
2346 rb: impl Into<StackIdxConv>,
2347 rc: impl Into<StackIdxConv>,
2348 ) -> Option<(i64, i64)> {
2349 let rb: StackIdx = rb.into().0;
2350 let rc: StackIdx = rc.into().0;
2351 match (
2352 self.stack[rb.0 as usize].val,
2353 self.stack[rc.0 as usize].val,
2354 ) {
2355 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib, ic)),
2356 _ => None,
2357 }
2358 }
2359 #[inline(always)]
2364 pub fn get_num_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2365 let i: StackIdx = idx.into().0;
2366 match self.stack.get(i.0 as usize) {
2367 Some(slot) => match &slot.val {
2368 LuaValue::Float(f) => Some(*f),
2369 LuaValue::Int(v) => Some(*v as f64),
2370 _ => None,
2371 },
2372 None => None,
2373 }
2374 }
2375 #[inline(always)]
2380 pub fn get_float_at(&self, idx: impl Into<StackIdxConv>) -> Option<f64> {
2381 let i: StackIdx = idx.into().0;
2382 match self.stack.get(i.0 as usize) {
2383 Some(slot) => match &slot.val {
2384 LuaValue::Float(f) => Some(*f),
2385 _ => None,
2386 },
2387 None => None,
2388 }
2389 }
2390 #[inline(always)]
2395 pub fn get_num_pair_at(
2396 &self,
2397 rb: impl Into<StackIdxConv>,
2398 rc: impl Into<StackIdxConv>,
2399 ) -> Option<(f64, f64)> {
2400 let rb: StackIdx = rb.into().0;
2401 let rc: StackIdx = rc.into().0;
2402 match (
2403 self.stack[rb.0 as usize].val,
2404 self.stack[rc.0 as usize].val,
2405 ) {
2406 (LuaValue::Float(nb), LuaValue::Float(nc)) => Some((nb, nc)),
2407 (LuaValue::Int(ib), LuaValue::Int(ic)) => Some((ib as f64, ic as f64)),
2408 (LuaValue::Int(ib), LuaValue::Float(nc)) => Some((ib as f64, nc)),
2409 (LuaValue::Float(nb), LuaValue::Int(ic)) => Some((nb, ic as f64)),
2410 _ => None,
2411 }
2412 }
2413 #[inline(always)]
2426 pub fn set_top(&mut self, idx: impl Into<StackIdxConv>) {
2427 let new_top: StackIdx = idx.into().0;
2428 let new_top_u = new_top.0 as usize;
2429 if new_top_u > self.stack.len() {
2430 self.stack.resize_with(new_top_u, StackValue::default);
2431 }
2432 self.top = new_top;
2433 }
2434 #[inline(always)]
2440 pub fn set_top_idx(&mut self, idx: impl Into<StackIdxConv>) {
2441 let new_top: StackIdx = idx.into().0;
2442 self.top = new_top;
2443 }
2444 #[inline(always)]
2447 pub fn dec_top(&mut self) {
2448 if self.top.0 > 0 {
2449 self.top = StackIdx(self.top.0 - 1);
2450 }
2451 }
2452 #[inline(always)]
2453 pub fn pop_n(&mut self, n: usize) {
2454 let cur = self.top.0 as usize;
2455 let new = cur.saturating_sub(n);
2456 self.top = StackIdx(new as u32);
2457 }
2458 #[inline(always)]
2461 pub fn peek_at(&mut self, idx: impl Into<StackIdxConv>) -> LuaValue {
2462 let i: StackIdx = idx.into().0;
2463 match self.stack.get(i.0 as usize) {
2464 Some(slot) => slot.val.clone(),
2465 None => LuaValue::Nil,
2466 }
2467 }
2468 #[inline(always)]
2472 pub fn peek_top(&mut self) -> LuaValue {
2473 if self.top.0 == 0 {
2474 return LuaValue::Nil;
2475 }
2476 self.stack[(self.top.0 - 1) as usize].val.clone()
2477 }
2478 pub fn peek_string_at_top(&mut self) -> GcRef<LuaString> {
2483 match self.peek_top() {
2484 LuaValue::Str(s) => s,
2485 _ => panic!("peek_string_at_top: top of stack is not a string"),
2486 }
2487 }
2488 pub fn stack_at(&mut self, idx: impl Into<StackIdxConv>) -> &mut LuaValue {
2491 let i: StackIdx = idx.into().0;
2492 &mut self.stack[i.0 as usize].val
2493 }
2494 pub fn stack_set_nil(&mut self, idx: impl Into<StackIdxConv>) {
2497 let i: StackIdx = idx.into().0;
2498 let slot = i.0 as usize;
2499 if slot < self.stack.len() {
2500 self.stack[slot].val = LuaValue::Nil;
2501 }
2502 }
2503 pub fn stack_resize(&mut self, size: usize) -> Result<(), LuaError> {
2511 self.stack.resize_with(size, StackValue::default);
2512 Ok(())
2513 }
2514 pub fn stack_available(&mut self) -> usize {
2515 (self.stack_last.0 as usize).saturating_sub(self.top.0 as usize)
2516 }
2517 pub fn check_stack(&mut self, n: i32) -> Result<(), LuaError> {
2518 let free = (self.stack_last.0 as i32) - (self.top.0 as i32);
2519 if free <= n {
2520 self.grow_stack(n, true)?;
2521 }
2522 Ok(())
2523 }
2524 pub fn grow_stack(&mut self, n: i32, raise_error: bool) -> Result<(), LuaError> {
2533 crate::do_::grow_stack(self, n, raise_error).map(|_| ())
2534 }
2535
2536 #[inline(always)]
2537 pub fn get_ci(&self, idx: CallInfoIdx) -> &CallInfo { &self.call_info[idx.as_usize()] }
2538 #[inline(always)]
2539 pub fn get_ci_mut(&mut self, idx: CallInfoIdx) -> &mut CallInfo { &mut self.call_info[idx.as_usize()] }
2540 #[inline(always)]
2541 pub fn current_call_info(&self) -> &CallInfo { &self.call_info[self.ci.as_usize()] }
2542 #[inline(always)]
2543 pub fn current_call_info_mut(&mut self) -> &mut CallInfo { let i = self.ci.as_usize(); &mut self.call_info[i] }
2544 #[inline(always)]
2545 pub fn current_ci_idx(&self) -> CallInfoIdx { self.ci }
2546 pub fn call_stack_mut(&mut self) -> &mut Vec<CallInfo> { &mut self.call_info }
2547 #[inline(always)]
2548 pub fn next_ci(&mut self) -> Result<CallInfoIdx, LuaError> {
2549 match self.call_info[self.ci.as_usize()].next {
2550 Some(idx) => Ok(idx),
2551 None => Ok(extend_ci(self)),
2552 }
2553 }
2554 #[inline(always)]
2555 pub fn prev_ci(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2556 pub fn get_prev_ci(&self, idx: CallInfoIdx) -> Option<&CallInfo> {
2557 self.call_info[idx.as_usize()]
2558 .previous
2559 .map(|p| &self.call_info[p.as_usize()])
2560 }
2561 #[inline(always)]
2562 pub fn is_base_ci(&self, idx: CallInfoIdx) -> bool { idx.as_usize() == 0 }
2563 #[inline(always)]
2564 pub fn is_current_ci(&self, idx: CallInfoIdx) -> bool { idx == self.ci }
2565 pub fn ci_next_func(&self, idx: CallInfoIdx) -> StackIdx {
2566 let next = self.call_info[idx.as_usize()]
2567 .next
2568 .expect("ci_next_func: no next CallInfo");
2569 self.call_info[next.as_usize()].func
2570 }
2571 #[inline(always)]
2572 pub fn ci_top(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].top }
2573 #[inline(always)]
2574 pub fn ci_trap(&mut self, idx: CallInfoIdx) -> bool {
2575 if let CallInfoFrame::Lua { trap, .. } = self.call_info[idx.as_usize()].u {
2576 trap
2577 } else {
2578 false
2579 }
2580 }
2581 #[inline(always)]
2582 pub fn ci_savedpc(&self, idx: CallInfoIdx) -> u32 { self.call_info[idx.as_usize()].saved_pc() }
2583 #[inline(always)]
2584 pub fn set_ci_savedpc(&mut self, idx: CallInfoIdx, pc: u32) {
2585 self.call_info[idx.as_usize()].set_saved_pc(pc);
2586 }
2587 #[inline(always)]
2588 pub fn set_ci_previous(&mut self, idx: CallInfoIdx) {
2589 self.ci = self.call_info[idx.as_usize()]
2590 .previous
2591 .expect("set_ci_previous: returning frame has no previous CallInfo");
2592 }
2593 #[inline(always)]
2594 pub fn ci_previous(&self, idx: CallInfoIdx) -> Option<CallInfoIdx> { self.call_info[idx.as_usize()].previous }
2595 #[inline(always)]
2596 pub fn ci_adjust_func(&mut self, idx: CallInfoIdx, delta: i32) {
2597 let ci = &mut self.call_info[idx.as_usize()];
2598 ci.func = StackIdx((ci.func.0 as i32 - delta) as u32);
2599 }
2600 #[inline(always)]
2601 pub fn ci_base(&self, idx: CallInfoIdx) -> StackIdx { self.call_info[idx.as_usize()].func + 1 }
2602 #[inline(always)]
2603 pub fn ci_is_fresh(&self, idx: CallInfoIdx) -> bool {
2604 (self.call_info[idx.as_usize()].callstatus & CIST_FRESH) != 0
2605 }
2606 #[inline(always)]
2607 pub fn ci_lua_closure(&self, idx: CallInfoIdx) -> Option<GcRef<lua_types::closure::LuaLClosure>> {
2608 let func_idx = self.call_info[idx.as_usize()].func;
2609 match self.stack.get(func_idx.0 as usize).map(|slot| slot.val) {
2610 Some(LuaValue::Function(lua_types::closure::LuaClosure::Lua(cl))) => Some(cl),
2611 _ => None,
2612 }
2613 }
2614 #[inline(always)]
2615 pub fn ci_nextraargs(&self, idx: CallInfoIdx) -> i32 {
2616 self.call_info[idx.as_usize()].nextra_args()
2617 }
2618 #[inline(always)]
2619 pub fn ci_nres(&self, idx: CallInfoIdx) -> i32 {
2620 self.call_info[idx.as_usize()].u2.value
2621 }
2622 #[inline(always)]
2623 pub fn ci_nres_set(&mut self, idx: CallInfoIdx, n: i32) {
2624 self.call_info[idx.as_usize()].u2.value = n;
2625 }
2626 #[inline(always)]
2627 pub fn ci_nresults(&self, idx: CallInfoIdx) -> i32 { self.call_info[idx.as_usize()].nresults as i32 }
2628 pub fn ci_prev_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2629 let pc = self.call_info[idx.as_usize()].saved_pc();
2630 let cl = self.ci_lua_closure(idx)
2631 .expect("ci_prev_instruction: CallInfo does not hold a Lua closure");
2632 cl.proto.code[(pc - 1) as usize]
2633 }
2634 pub fn ci_prev2_instruction(&self, idx: CallInfoIdx) -> lua_types::opcode::Instruction {
2635 let pc = self.call_info[idx.as_usize()].saved_pc();
2636 let cl = self.ci_lua_closure(idx)
2637 .expect("ci_prev2_instruction: CallInfo does not hold a Lua closure");
2638 cl.proto.code[(pc - 2) as usize]
2639 }
2640 pub fn ci_skip_next_instruction(&mut self, idx: CallInfoIdx) {
2641 let pc = self.call_info[idx.as_usize()].saved_pc();
2642 self.call_info[idx.as_usize()].set_saved_pc(pc + 1);
2643 }
2644 pub fn ci_step_pc_back(&mut self, idx: CallInfoIdx) {
2645 let pc = self.call_info[idx.as_usize()].saved_pc();
2646 self.call_info[idx.as_usize()].set_saved_pc(pc - 1);
2647 }
2648 pub fn get_ci_pcrel(&mut self, idx: CallInfoIdx) -> u32 {
2649 self.call_info[idx.as_usize()].saved_pc().saturating_sub(1)
2650 }
2651 pub fn get_ci_u2_funcidx(&mut self, idx: CallInfoIdx) -> i32 {
2652 self.call_info[idx.as_usize()].u2.value
2653 }
2654 pub fn get_ci_u2_nres(&mut self, idx: CallInfoIdx) -> i32 {
2655 self.call_info[idx.as_usize()].u2.value
2656 }
2657 pub fn get_ci_u2_nyield(&mut self, idx: CallInfoIdx) -> i32 {
2658 self.call_info[idx.as_usize()].u2.value
2659 }
2660 pub fn get_ci_vararg_info(&mut self, idx: CallInfoIdx) -> (bool, i32, i32) {
2661 let nextraargs = self.call_info[idx.as_usize()].nextra_args();
2662 match self.ci_lua_closure(idx) {
2663 Some(cl) => (cl.proto.is_vararg, nextraargs, cl.proto.numparams as i32),
2664 None => (false, nextraargs, 0),
2665 }
2666 }
2667 pub fn get_ci_lua_proto_numparams(&mut self, idx: CallInfoIdx) -> u8 {
2668 self.ci_lua_closure(idx)
2669 .map(|cl| cl.proto.numparams)
2670 .unwrap_or(0)
2671 }
2672 pub fn set_ci_u2_nres(&mut self, idx: CallInfoIdx, n: i32) {
2673 self.call_info[idx.as_usize()].u2.value = n;
2674 }
2675 pub fn set_ci_u2_nyield(&mut self, idx: CallInfoIdx, n: i32) {
2676 self.call_info[idx.as_usize()].u2.value = n;
2677 }
2678 pub fn set_ci_transfer_info(&mut self, idx: CallInfoIdx, ftransfer: u16, ntransfer: u16) {
2679 let ci = &mut self.call_info[idx.as_usize()];
2680 ci.u2.ftransfer = ftransfer;
2681 ci.u2.ntransfer = ntransfer;
2682 }
2683 pub fn shrink_ci(&mut self) { shrink_ci(self) }
2684 pub fn check_c_stack(&mut self) -> Result<(), LuaError> { check_c_stack(self) }
2685
2686 pub fn status(&mut self) -> LuaStatus { LuaStatus::from_raw(self.status as i32) }
2687 pub fn errfunc(&mut self) -> isize { self.errfunc }
2688 pub fn old_pc(&mut self) -> u32 { self.oldpc }
2689 pub fn set_old_pc(&mut self, pc: u32) { self.oldpc = pc; }
2690 pub fn set_oldpc(&mut self, pc: u32) { self.oldpc = pc; }
2691 pub fn _hook_call_noargs(&mut self) {}
2692 pub fn hook(&self) -> Option<&Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>> {
2693 self.hook.as_ref()
2694 }
2695 pub fn has_hook(&mut self) -> bool { self.hook.is_some() }
2696 pub fn hook_count(&mut self) -> i32 { self.hookcount }
2697 pub fn set_hook_count(&mut self, n: i32) { self.hookcount = n; }
2698 pub fn hook_mask(&self) -> u8 { self.hookmask }
2699 pub fn set_hook_mask(&mut self, m: u8) { self.hookmask = m; }
2700 pub fn base_hook_count(&self) -> i32 { self.basehookcount }
2701 pub fn set_base_hook_count(&mut self, n: i32) { self.basehookcount = n; }
2702 pub fn set_hook(&mut self, h: Option<Box<dyn FnMut(&mut LuaState, &crate::debug::LuaDebug)>>) {
2703 self.hook = h;
2704 }
2705 pub fn call_hook_event(&mut self, event: i32, line: i32) -> Result<(), LuaError> {
2706 crate::do_::hook(self, event, line, 0, 0)
2707 }
2708
2709 pub fn registry_value(&self) -> LuaValue { self.global().l_registry.clone() }
2710 pub fn registry_get(&self, key: usize) -> LuaValue {
2711 let reg = self.global().l_registry.clone();
2712 match reg {
2713 LuaValue::Table(t) => t.get(&LuaValue::Int(key as i64)),
2714 _ => LuaValue::Nil,
2715 }
2716 }
2717
2718 pub fn new_string(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> { self.intern_or_create_str(bytes) }
2719
2720 pub fn new_proto(&mut self) -> GcRef<LuaProto> {
2732 self.mark_gc_check_needed();
2733 GcRef::new(LuaProto::placeholder())
2734 }
2735
2736 pub fn new_lclosure(&mut self, proto: GcRef<LuaProto>, nupvals: usize) -> GcRef<LuaClosureLua> {
2738 self.mark_gc_check_needed();
2739 let mut upvals = Vec::with_capacity(nupvals);
2740 for _ in 0..nupvals {
2741 upvals.push(std::cell::Cell::new(self.new_upval_closed(LuaValue::Nil)));
2742 }
2743 let closure = GcRef::new(LuaClosureLua { proto, upvals });
2744 closure.account_buffer(closure.buffer_bytes() as isize);
2745 closure
2746 }
2747
2748 pub fn new_upval_closed(&mut self, v: LuaValue) -> GcRef<UpVal> {
2750 self.mark_gc_check_needed();
2751 GcRef::new(UpVal::closed(v))
2752 }
2753
2754 pub fn new_upval_open(&mut self, thread_id: usize, level: StackIdx) -> GcRef<UpVal> {
2756 self.mark_gc_check_needed();
2757 GcRef::new(UpVal::open(thread_id, level))
2758 }
2759 pub fn intern_or_create_str(&mut self, bytes: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
2766 self.intern_str(bytes)
2767 }
2768 pub fn new_userdata(&mut self, _size: usize, _nuvalue: usize) -> Result<GcRef<LuaUserData>, LuaError> {
2769 Err(LuaError::runtime(format_args!("new_userdata not implemented in this Phase-B build; use new_userdata_typed instead")))
2770 }
2771 pub fn new_c_closure(&mut self, _f: LuaCFunction, _n: i32) -> Result<LuaClosure, LuaError> {
2772 Err(LuaError::runtime(format_args!("new_c_closure not implemented in this Phase-B build; use push_cclosure in lua_vm::api instead")))
2773 }
2774 pub fn push_closure(
2775 &mut self,
2776 proto_idx: usize,
2777 ci: CallInfoIdx,
2778 base: StackIdx,
2779 ra: StackIdx,
2780 ) -> Result<(), LuaError> {
2781 let parent_cl = self.ci_lua_closure(ci).expect(
2782 "push_closure: current frame is not a Lua closure",
2783 );
2784 let child_proto = parent_cl.proto.p[proto_idx].clone();
2785 let nup = child_proto.upvalues.len();
2786 let mut upvals: Vec<std::cell::Cell<GcRef<UpVal>>> = Vec::with_capacity(nup);
2787 for i in 0..nup {
2788 let desc = &child_proto.upvalues[i];
2789 let uv = if desc.instack {
2790 let level = base + desc.idx as i32;
2791 crate::func::find_upval(self, level)
2792 } else {
2793 parent_cl.upval(desc.idx as usize)
2794 };
2795 upvals.push(std::cell::Cell::new(uv));
2796 }
2797 let cache_enabled = matches!(
2801 self.global().lua_version,
2802 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53
2803 );
2804 if cache_enabled {
2805 if let Some(cached) = child_proto.cache.borrow().as_ref() {
2806 if cached.upvals.len() == nup
2807 && (0..nup).all(|i| GcRef::ptr_eq(&cached.upvals[i].get(), &upvals[i].get()))
2808 {
2809 let reused = cached.clone();
2810 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(reused)));
2811 return Ok(());
2812 }
2813 }
2814 }
2815 self.mark_gc_check_needed();
2818 let new_cl = GcRef::new(LuaClosureLua {
2819 proto: child_proto.clone(),
2820 upvals,
2821 });
2822 new_cl.account_buffer(new_cl.buffer_bytes() as isize);
2823 if cache_enabled {
2824 *child_proto.cache.borrow_mut() = Some(new_cl.clone());
2825 }
2826 self.set_at(ra, LuaValue::Function(LuaClosure::Lua(new_cl)));
2827 Ok(())
2828 }
2829 pub fn new_tbc_upval(&mut self, idx: StackIdx) -> Result<(), LuaError> {
2830 crate::func::new_tbc_upval(self, idx)
2831 }
2832
2833 #[inline(always)]
2854 pub fn upvalue_get(&self, cl: &GcRef<LuaClosureLua>, n: usize) -> LuaValue {
2855 let uv = cl.upval(n);
2856 let (thread_id, idx) = match uv.try_open_payload() {
2857 Some(p) => p,
2858 None => return uv.closed_value(),
2859 };
2860 let current = self.cached_thread_id;
2861 let tid = thread_id as u64;
2862 if tid == current {
2863 return self.stack[idx.0 as usize].val;
2864 }
2865 self.upvalue_get_cross_thread(tid, idx)
2866 }
2867
2868 #[cold]
2869 #[inline(never)]
2870 fn upvalue_get_cross_thread(&self, tid: u64, idx: StackIdx) -> LuaValue {
2871 let entry_rc = {
2872 let g = self.global();
2873 g.threads.get(&tid).map(|e| e.state.clone())
2874 };
2875 if let Some(rc) = entry_rc {
2876 if let Ok(home_state) = rc.try_borrow() {
2877 return home_state.get_at(idx);
2878 }
2879 }
2880 let g = self.global();
2881 match g.cross_thread_upvals.get(&(tid, idx)) {
2882 Some(v) => *v,
2883 None => LuaValue::Nil,
2884 }
2885 }
2886 #[inline(always)]
2895 pub fn upvalue_set(&mut self, cl: &GcRef<LuaClosureLua>, n: usize, val: LuaValue) -> Result<(), LuaError> {
2896 let uv = cl.upval(n);
2897 match uv.try_open_payload() {
2898 Some((thread_id, idx)) => {
2899 let tid = thread_id as u64;
2900 let current = self.cached_thread_id;
2901 if tid == current {
2902 self.stack[idx.0 as usize].val = val;
2903 } else {
2904 self.upvalue_set_cross_thread(tid, idx, val)?;
2905 }
2906 }
2907 None => {
2908 uv.set_closed_value(val);
2909 }
2910 }
2911 if val.is_collectable() {
2912 self.gc_barrier_upval(&uv, &val);
2913 }
2914 Ok(())
2915 }
2916
2917 #[cold]
2918 #[inline(never)]
2919 fn upvalue_set_cross_thread(
2920 &mut self,
2921 tid: u64,
2922 idx: StackIdx,
2923 val: LuaValue,
2924 ) -> Result<(), LuaError> {
2925 let entry_rc = {
2926 let g = self.global();
2927 g.threads.get(&tid).map(|e| e.state.clone())
2928 };
2929 if let Some(rc) = entry_rc {
2930 if let Ok(mut home_state) = rc.try_borrow_mut() {
2931 home_state.set_at(idx, val);
2932 return Ok(());
2933 }
2934 }
2935 let mut g = self.global_mut();
2936 g.cross_thread_upvals.insert((tid, idx), val);
2937 Ok(())
2938 }
2939
2940 pub fn protected_call_raw(&mut self, func: StackIdx, nresults: i32, errfunc: StackIdx) -> Result<(), LuaError> {
2941 let ef = errfunc.0 as isize;
2942 let status = crate::do_::pcall(
2943 self,
2944 |s| s.call_no_yield(func, nresults),
2945 func,
2946 ef,
2947 );
2948 match status {
2949 LuaStatus::Ok => Ok(()),
2950 LuaStatus::ErrSyntax => {
2951 let err_val = self.get_at(func);
2952 self.set_top(func);
2953 Err(LuaError::Syntax(err_val))
2954 }
2955 LuaStatus::Yield => {
2956 self.set_top(func);
2957 Err(LuaError::Yield)
2958 }
2959 _ => {
2960 let err_val = self.get_at(func);
2961 self.set_top(func);
2962 Err(LuaError::Runtime(err_val))
2963 }
2964 }
2965 }
2966 pub fn protected_parser(&mut self, z: crate::zio::ZIO, name: &[u8], mode: Option<&[u8]>) -> LuaStatus {
2967 crate::do_::protected_parser(self, z, name, mode)
2968 }
2969 pub fn do_call(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2970 crate::do_::call(self, func, nresults)
2971 }
2972 pub fn do_call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2973 crate::do_::callnoyield(self, func, nresults)
2974 }
2975 pub fn call_no_yield(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2976 crate::do_::callnoyield(self, func, nresults)
2977 }
2978 pub fn call_at(&mut self, func: StackIdx, nresults: i32) -> Result<(), LuaError> {
2979 crate::do_::call(self, func, nresults)
2980 }
2981 #[inline(always)]
2982 pub fn precall(&mut self, func: StackIdx, nresults: i32) -> Result<Option<CallInfoIdx>, LuaError> {
2983 crate::do_::precall(self, func, nresults)
2984 }
2985 #[inline(always)]
2986 pub fn pretailcall(
2987 &mut self,
2988 ci: CallInfoIdx,
2989 func: StackIdx,
2990 narg1: i32,
2991 delta: i32,
2992 ) -> Result<i32, LuaError> {
2993 crate::do_::pretailcall(self, ci, func, narg1, delta)
2994 }
2995 #[inline(always)]
2996 pub fn poscall<N: TryInto<i32>>(&mut self, ci: CallInfoIdx, nres: N) -> Result<(), LuaError>
2997 where
2998 <N as TryInto<i32>>::Error: std::fmt::Debug,
2999 {
3000 let n = nres.try_into().expect("poscall: nres out of i32 range");
3001 crate::do_::poscall(self, ci, n)
3002 }
3003 pub fn adjust_results(&mut self, nresults: i32) {
3004 const LUA_MULTRET: i32 = -1;
3005 if nresults <= LUA_MULTRET {
3006 let ci_idx = self.ci.as_usize();
3007 if self.call_info[ci_idx].top.0 < self.top.0 {
3008 self.call_info[ci_idx].top = self.top;
3009 }
3010 }
3011 }
3012 pub fn adjust_varargs(
3013 &mut self,
3014 ci: CallInfoIdx,
3015 nfixparams: i32,
3016 cl: &GcRef<lua_types::closure::LuaLClosure>,
3017 ) -> Result<(), LuaError> {
3018 crate::tagmethods::adjust_varargs(self, nfixparams, ci, &cl.0.proto)
3019 }
3020 pub fn get_varargs(
3021 &mut self,
3022 ci: CallInfoIdx,
3023 ra: StackIdx,
3024 n: i32,
3025 ) -> Result<i32, LuaError> {
3026 crate::tagmethods::get_varargs(self, ci, ra, n)?;
3027 Ok(0)
3028 }
3029
3030 pub fn close_upvals(&mut self, level: StackIdx) -> Result<(), LuaError> {
3031 crate::func::close_upval(self, level);
3032 Ok(())
3033 }
3034 pub fn close_upvals_status(&mut self, level: StackIdx, _status: i32) -> Result<(), LuaError> {
3035 crate::func::close_upval(self, level);
3036 Ok(())
3037 }
3038 pub fn close_upvals_from_base(&mut self, ci: CallInfoIdx) -> Result<(), LuaError> {
3039 let base = self.ci_base(ci);
3040 crate::func::close_upval(self, base);
3041 Ok(())
3042 }
3043
3044 pub fn arith_op(&mut self, op: i32, p1: &LuaValue, p2: &LuaValue) -> Result<LuaValue, LuaError> {
3045 let arith_op = match op {
3046 0 => lua_types::arith::ArithOp::Add,
3047 1 => lua_types::arith::ArithOp::Sub,
3048 2 => lua_types::arith::ArithOp::Mul,
3049 3 => lua_types::arith::ArithOp::Mod,
3050 4 => lua_types::arith::ArithOp::Pow,
3051 5 => lua_types::arith::ArithOp::Div,
3052 6 => lua_types::arith::ArithOp::Idiv,
3053 7 => lua_types::arith::ArithOp::Band,
3054 8 => lua_types::arith::ArithOp::Bor,
3055 9 => lua_types::arith::ArithOp::Bxor,
3056 10 => lua_types::arith::ArithOp::Shl,
3057 11 => lua_types::arith::ArithOp::Shr,
3058 12 => lua_types::arith::ArithOp::Unm,
3059 13 => lua_types::arith::ArithOp::Bnot,
3060 _ => return Err(LuaError::runtime(format_args!("invalid arith op {}", op))),
3061 };
3062 let mut res = LuaValue::Nil;
3063 if crate::object::raw_arith(self, arith_op, p1, p2, &mut res)? {
3064 Ok(res)
3065 } else {
3066 Err(LuaError::arith_error(p1, p2, "perform arithmetic on"))
3067 }
3068 }
3069 pub fn concat(&mut self, n: i32) -> Result<(), LuaError> {
3070 crate::vm::concat(self, n)
3071 }
3072 pub fn less_than(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3073 crate::vm::less_than(self, l, r)
3074 }
3075 pub fn less_equal(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3076 crate::vm::less_equal(self, l, r)
3077 }
3078 pub fn equal_obj(&self, _ctx: Option<&LuaValue>, l: &LuaValue, r: &LuaValue) -> bool {
3079 crate::vm::equal_obj(None, l, r).unwrap_or(false)
3080 }
3081 pub fn equal_obj_with_tm(&mut self, l: &LuaValue, r: &LuaValue) -> Result<bool, LuaError> {
3082 crate::vm::equal_obj(Some(self), l, r)
3083 }
3084 pub fn obj_len(&mut self, v: &LuaValue) -> Result<LuaValue, LuaError> {
3085 match v {
3086 LuaValue::Table(_) => {
3087 let consult_len_tm =
3090 !matches!(self.global().lua_version, lua_types::LuaVersion::V51);
3091 let tm = if consult_len_tm {
3092 let mt = self.table_metatable(v);
3093 self.fast_tm_table(mt.as_ref(), TagMethod::Len)
3094 } else {
3095 LuaValue::Nil
3096 };
3097 if matches!(tm, LuaValue::Nil) {
3098 let n = self.table_length(v)?;
3099 return Ok(LuaValue::Int(n));
3100 }
3101 self.push(LuaValue::Nil);
3102 let slot = StackIdx(self.top.0 - 1);
3103 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3104 Ok(self.pop())
3105 }
3106 LuaValue::Str(s) => Ok(LuaValue::Int(s.len() as i64)),
3107 other => {
3108 let tm = crate::tagmethods::get_tm_by_obj(self, other, crate::tagmethods::TagMethod::Len);
3109 if matches!(tm, LuaValue::Nil) {
3110 let mut msg = b"attempt to get length of a ".to_vec();
3111 msg.extend_from_slice(&self.obj_type_name(other));
3112 msg.extend_from_slice(b" value");
3113 return Err(crate::debug::prefixed_runtime_pub(self, msg));
3114 }
3115 self.push(LuaValue::Nil);
3116 let slot = StackIdx(self.top.0 - 1);
3117 crate::tagmethods::call_tm_res(self, tm, v.clone(), v.clone(), slot)?;
3118 Ok(self.pop())
3119 }
3120 }
3121 }
3122 pub fn obj_to_string(&mut self, idx: i32) -> Result<GcRef<LuaString>, LuaError> {
3123 let slot: StackIdx = if idx > 0 {
3124 let ci_func = self.current_call_info().func;
3125 ci_func + idx
3126 } else {
3127 debug_assert!(idx != 0, "invalid index");
3128 StackIdx((self.top_idx().0 as i32 + idx) as u32)
3129 };
3130 let val = self.get_at(slot);
3131 match val {
3132 LuaValue::Str(s) => Ok(s),
3133 LuaValue::Int(_) | LuaValue::Float(_) => {
3134 let s = crate::object::num_to_string(self, &val)?;
3135 self.set_at(slot, LuaValue::Str(s.clone()));
3136 Ok(s)
3137 }
3138 _ => Err(LuaError::type_error(&val, "convert to string")),
3139 }
3140 }
3141 pub fn coerce_to_string(&mut self, idx: StackIdx) -> Result<GcRef<LuaString>, LuaError> {
3142 let val = self.get_at(idx);
3143 match val {
3144 LuaValue::Str(s) => Ok(s),
3145 LuaValue::Int(_) | LuaValue::Float(_) => {
3146 let s = crate::object::num_to_string(self, &val)?;
3147 self.set_at(idx, LuaValue::Str(s.clone()));
3148 Ok(s)
3149 }
3150 _ => Err(LuaError::type_error(&val, "convert to string")),
3151 }
3152 }
3153 pub fn str_to_num(&mut self, s: &[u8]) -> Option<(LuaValue, usize)> {
3154 let mut out = LuaValue::Nil;
3155 let sz = crate::object::str2num(s, &mut out);
3156 if sz == 0 { None } else { Some((out, sz)) }
3157 }
3158
3159 #[inline(always)]
3160 pub fn fast_get(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3161 let LuaValue::Table(tbl) = t else { return Ok(None); };
3162 let v = tbl.get(k);
3163 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3164 }
3165 #[inline(always)]
3166 pub fn fast_get_int(&mut self, t: &LuaValue, k: i64) -> Result<Option<LuaValue>, LuaError> {
3167 let LuaValue::Table(tbl) = t else { return Ok(None); };
3168 let v = tbl.get_int(k);
3169 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3170 }
3171 #[inline(always)]
3172 pub fn fast_get_short_str(&mut self, t: &LuaValue, k: &LuaValue) -> Result<Option<LuaValue>, LuaError> {
3173 let LuaValue::Table(tbl) = t else { return Ok(None); };
3174 let LuaValue::Str(s) = k else { return Ok(None); };
3175 let v = tbl.get_short_str(s);
3176 if matches!(v, LuaValue::Nil) { Ok(None) } else { Ok(Some(v)) }
3177 }
3178 #[inline(always)]
3179 pub fn fast_tm_table(&mut self, t: Option<&GcRef<LuaTable>>, tm: TagMethod) -> LuaValue {
3180 let Some(mt) = t else { return LuaValue::Nil; };
3181 debug_assert!((tm as u8) <= TagMethod::Eq as u8);
3182 let ename = self.global().tmname[tm as usize].clone();
3183 mt.get_short_str(&ename)
3184 }
3185 pub fn fast_tm_ud(&mut self, u: &GcRef<LuaUserData>, tm: TagMethod) -> LuaValue {
3186 let mt = u.metatable();
3188 self.fast_tm_table(mt.as_ref(), tm)
3189 }
3190
3191 pub fn table_get_with_tm(&mut self, t: &LuaValue, k: &LuaValue) -> Result<LuaValue, LuaError> {
3192 if let LuaValue::Table(tbl) = t {
3198 if tbl.metatable().is_none() {
3199 return Ok(tbl.get(k));
3200 }
3201 }
3202 if let Some(v) = self.fast_get(t, k)? {
3203 return Ok(v);
3204 }
3205 let res = self.top_idx();
3206 self.push(LuaValue::Nil);
3207 crate::vm::finish_get(self, t.clone(), k.clone(), res, true, None)?;
3208 let value = self.get_at(res);
3209 self.pop();
3210 Ok(value)
3211 }
3212 #[inline]
3227 pub fn table_set_with_tm(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3228 if let LuaValue::Table(tbl) = t {
3229 if tbl.metatable().is_none() {
3230 self.gc_table_barrier_back(tbl, &v);
3231 return self.table_raw_set(t, k, v);
3232 }
3233 }
3234 if self.fast_get(t, &k)?.is_some() {
3235 self.gc_value_barrier_back(t, &v);
3236 return self.table_raw_set(t, k, v);
3237 }
3238 crate::vm::finish_set(self, t.clone(), k, v, true, None, None)
3239 }
3240 #[inline]
3241 pub fn table_raw_set(&mut self, t: &LuaValue, k: LuaValue, v: LuaValue) -> Result<(), LuaError> {
3242 let LuaValue::Table(tbl) = t else {
3243 return Err(LuaError::type_error(t, "index"));
3244 };
3245 let tbl = tbl.clone();
3246 tbl.raw_set(self, k, v)
3247 }
3248 #[inline]
3249 pub fn table_array_set(&mut self, t: &LuaValue, idx: usize, v: LuaValue) -> Result<(), LuaError> {
3250 let LuaValue::Table(tbl) = t else {
3251 return Err(LuaError::type_error(t, "index"));
3252 };
3253 let tbl = tbl.clone();
3254 tbl.raw_set_int(self, idx as i64 + 1, v)
3255 }
3256 pub fn table_ensure_array(&mut self, t: &LuaValue, n: usize) -> Result<(), LuaError> {
3257 let LuaValue::Table(tbl) = t else {
3258 return Err(LuaError::type_error(t, "index"));
3259 };
3260 if n > tbl.array_len() {
3261 tbl.resize(self, n, 0)?;
3262 }
3263 Ok(())
3264 }
3265 pub fn table_length(&mut self, t: &LuaValue) -> Result<i64, LuaError> {
3266 let LuaValue::Table(tbl) = t else {
3267 return Err(LuaError::type_error(t, "get length of"));
3268 };
3269 Ok(tbl.getn() as i64)
3270 }
3271 pub fn table_metatable(&mut self, v: &LuaValue) -> Option<GcRef<LuaTable>> {
3272 match v {
3273 LuaValue::Table(t) => t.metatable(),
3274 LuaValue::UserData(u) => u.metatable(),
3275 other => {
3276 let idx = other.base_type() as usize;
3277 self.global().mt[idx].clone()
3278 }
3279 }
3280 }
3281 pub fn table_resize(&mut self, t: &GcRef<LuaTable>, na: usize, nh: usize) -> Result<(), LuaError> {
3282 self.mark_gc_check_needed();
3283 t.resize(self, na, nh)
3284 }
3285 pub fn table_getn(&self, t: &GcRef<LuaTable>) -> i64 {
3286 let mut i: i64 = 1;
3294 loop {
3295 let v = t.get_int(i);
3296 if matches!(v, LuaValue::Nil) {
3297 return i - 1;
3298 }
3299 i += 1;
3300 }
3301 }
3302
3303 pub fn try_bin_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3304 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3305 crate::tagmethods::try_bin_tm(self, p1, p1_idx, p2, p2_idx, res, event)
3306 }
3307 pub fn try_bin_i_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, imm: i64, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3308 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3309 crate::tagmethods::try_bini_tm(self, p1, p1_idx, imm, flip, res, event)
3310 }
3311 pub fn try_bin_assoc_tm(&mut self, p1: &LuaValue, p1_idx: Option<StackIdx>, p2: &LuaValue, p2_idx: Option<StackIdx>, flip: bool, res: StackIdx, tm: lua_types::tagmethod::TagMethod) -> Result<(), LuaError> {
3312 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3313 crate::tagmethods::try_bin_assoc_tm(self, p1, p1_idx, p2, p2_idx, flip, res, event)
3314 }
3315 pub fn try_concat_tm(&mut self, _p1: &LuaValue, _p2: &LuaValue) -> Result<(), LuaError> {
3316 crate::tagmethods::try_concat_tm(self)
3317 }
3318 pub fn call_tm(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, p3: &LuaValue) -> Result<(), LuaError> {
3319 crate::tagmethods::call_tm(self, f, p1.clone(), p2.clone(), p3.clone())
3320 }
3321 pub fn call_tm_res(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue, res: StackIdx) -> Result<(), LuaError> {
3322 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)
3323 }
3324 pub fn call_tm_res_bool(&mut self, f: LuaValue, p1: &LuaValue, p2: &LuaValue) -> Result<bool, LuaError> {
3325 let res = self.top_idx();
3326 self.push(LuaValue::Nil);
3327 crate::tagmethods::call_tm_res(self, f, p1.clone(), p2.clone(), res)?;
3328 let result = self.get_at(res).clone();
3329 self.pop();
3330 Ok(!matches!(result, LuaValue::Nil | LuaValue::Bool(false)))
3331 }
3332 pub fn call_order_tm(&mut self, p1: &LuaValue, p2: &LuaValue, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3333 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3334 crate::tagmethods::call_order_tm(self, p1, p2, event)
3335 }
3336 pub fn call_order_i_tm(&mut self, p1: &LuaValue, v2: i64, flip: bool, isfloat: bool, tm: lua_types::tagmethod::TagMethod) -> Result<bool, LuaError> {
3337 let event = crate::tagmethods::TagMethod::from_u8(tm as u8);
3338 crate::tagmethods::call_orderi_tm(self, p1, v2 as i32, flip, isfloat, event)
3339 }
3340
3341 #[inline(always)]
3342 pub fn proto_code(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, pc: u32) -> lua_types::opcode::Instruction {
3343 cl.proto.code[pc as usize]
3344 }
3345 #[inline(always)]
3346 pub fn proto_const(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> LuaValue {
3347 cl.proto.k[idx].clone()
3348 }
3349 #[inline(always)]
3355 pub fn proto_const_int(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<i64> {
3356 match &cl.proto.k[idx] {
3357 LuaValue::Int(v) => Some(*v),
3358 _ => None,
3359 }
3360 }
3361 #[inline(always)]
3365 pub fn proto_const_num(&self, cl: &GcRef<lua_types::closure::LuaLClosure>, idx: usize) -> Option<f64> {
3366 match &cl.proto.k[idx] {
3367 LuaValue::Float(f) => Some(*f),
3368 LuaValue::Int(v) => Some(*v as f64),
3369 _ => None,
3370 }
3371 }
3372 pub fn get_proto_instr(&self, ci: CallInfoIdx, pc: u32) -> lua_types::opcode::Instruction {
3373 let cl = self.ci_lua_closure(ci)
3374 .expect("get_proto_instr: CallInfo does not hold a Lua closure");
3375 cl.proto.code[pc as usize]
3376 }
3377 pub fn trace_call(&mut self, _idx: CallInfoIdx) -> Result<bool, LuaError> {
3382 Ok(crate::debug::trace_call(self)? != 0)
3383 }
3384 pub fn trace_exec(&mut self, _idx: CallInfoIdx, pc: u32) -> Result<bool, LuaError> {
3387 Ok(crate::debug::trace_exec(self, pc)? != 0)
3388 }
3389 pub fn hook_call(&mut self, idx: CallInfoIdx) -> Result<(), LuaError> {
3390 crate::do_::hookcall(self, idx)
3391 }
3392 #[inline(always)]
3393 fn gc_step_flags(&self) -> Option<(bool, bool)> {
3394 let g = self.global();
3395 if !g.is_gc_running() {
3396 return None;
3397 }
3398 let should_collect = g.heap.would_collect();
3399 let has_finalizers = g.finalizers.has_to_be_finalized();
3400 if should_collect || has_finalizers {
3401 Some((should_collect, has_finalizers))
3402 } else {
3403 None
3404 }
3405 }
3406
3407 #[inline(always)]
3408 fn should_check_gc(&mut self) -> bool {
3409 if self.gc_check_needed {
3410 return true;
3411 }
3412 if self.global().finalizers.has_to_be_finalized() {
3413 self.gc_check_needed = true;
3414 return true;
3415 }
3416 false
3417 }
3418
3419 #[inline(always)]
3420 pub(crate) fn mark_gc_check_needed(&mut self) {
3421 self.gc_check_needed = true;
3422 }
3423
3424 #[inline(always)]
3425 pub fn gc_check_step(&mut self) {
3426 if !self.allowhook {
3427 return;
3428 }
3429 if !self.should_check_gc() {
3430 return;
3431 }
3432 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3433 self.gc_check_needed = false;
3434 return;
3435 };
3436 if should_collect || has_finalizers {
3437 if should_collect {
3438 self.gc().check_step();
3439 }
3440 crate::api::run_pending_finalizers(self);
3441 self.gc_check_needed = true;
3442 }
3443 let should_keep_checking = {
3444 let g = self.global();
3445 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3446 };
3447 self.gc_check_needed = should_keep_checking;
3448 }
3449 #[inline(always)]
3450 pub fn gc_cond_step(&mut self) {
3451 if !self.allowhook {
3452 return;
3453 }
3454 if !self.should_check_gc() {
3455 return;
3456 }
3457 let Some((should_collect, has_finalizers)) = self.gc_step_flags() else {
3458 self.gc_check_needed = false;
3459 return;
3460 };
3461 if should_collect || has_finalizers {
3462 if should_collect {
3463 self.gc().check_step();
3464 }
3465 crate::api::run_pending_finalizers(self);
3466 self.gc_check_needed = true;
3467 }
3468 let should_keep_checking = {
3469 let g = self.global();
3470 g.heap.would_collect() || g.finalizers.has_to_be_finalized()
3471 };
3472 self.gc_check_needed = should_keep_checking;
3473 }
3474 pub fn gc_barrier_back(&mut self, t: &dyn std::any::Any, v: &LuaValue) {
3475 self.gc().barrier_back(t, v);
3476 }
3477 pub fn gc_value_barrier_back(&mut self, t: &LuaValue, v: &LuaValue) {
3478 if let LuaValue::Table(tbl) = t {
3479 self.gc_table_barrier_back(tbl, v);
3480 } else {
3481 self.gc_barrier_back(t, v);
3482 }
3483 }
3484 pub fn gc_table_barrier_back(&mut self, t: &GcRef<LuaTable>, v: &LuaValue) {
3485 self.gc().table_barrier_back(t, v);
3486 }
3487 pub fn gc_barrier_upval(&mut self, uv: &GcRef<UpVal>, v: &LuaValue) {
3488 self.gc().barrier(uv, v);
3489 }
3490 pub fn is_main_thread(&mut self) -> bool {
3496 let g = self.global();
3497 g.current_thread_id == g.main_thread_id
3498 }
3499 pub fn obj_type_name<'v>(&self, v: &'v LuaValue) -> std::borrow::Cow<'static, [u8]> {
3500 match v {
3501 LuaValue::LightUserData(_) => std::borrow::Cow::Borrowed(b"light userdata"),
3502 LuaValue::Table(t) => {
3503 if let Some(mt) = t.metatable() {
3504 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3505 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3506 }
3507 }
3508 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3509 }
3510 LuaValue::UserData(u) => {
3511 if let Some(mt) = u.metatable() {
3512 if let LuaValue::Str(s) = mt.get_str_bytes(b"__name") {
3513 return std::borrow::Cow::Owned(s.as_bytes().to_vec());
3514 }
3515 }
3516 std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type()))
3517 }
3518 _ => std::borrow::Cow::Borrowed(crate::tagmethods::type_name(v.base_type())),
3519 }
3520 }
3521
3522 pub fn full_type_name(&mut self, v: &LuaValue) -> Result<Vec<u8>, LuaError> {
3523 crate::tagmethods::obj_type_name(self, v)
3524 }
3525 pub fn emit_warning(&mut self, _msg: &[u8], _to_cont: bool) { warning(self, _msg, _to_cont) }
3526}
3527
3528pub struct GcHandle<'a> {
3534 _state: &'a mut LuaState,
3535}
3536
3537struct CollectRoots<'a> {
3544 global: &'a GlobalState,
3545 thread: &'a LuaState,
3546}
3547
3548#[derive(Clone, Copy)]
3549enum HeapCollectMode {
3550 Full,
3551 Step,
3552 Minor,
3553}
3554
3555impl<'a> lua_gc::Trace for CollectRoots<'a> {
3556 fn trace(&self, m: &mut lua_gc::Marker) {
3557 self.global.trace(m);
3558 self.thread.trace(m);
3559 }
3560}
3561
3562#[derive(Clone, Copy)]
3563enum BarrierKind {
3564 Forward,
3565 Backward,
3566}
3567
3568fn barrier_lua_value<P>(
3569 heap: &lua_gc::Heap,
3570 parent: GcRef<P>,
3571 child: &LuaValue,
3572 generational: bool,
3573 kind: BarrierKind,
3574)
3575where
3576 P: lua_gc::Trace + 'static,
3577{
3578 if generational && matches!(kind, BarrierKind::Backward) {
3579 heap.generational_backward_barrier(parent.0);
3580 }
3581 match child {
3582 LuaValue::Str(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3583 LuaValue::Table(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3584 LuaValue::Function(LuaClosure::Lua(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3585 LuaValue::Function(LuaClosure::C(c)) => barrier_gc_child(heap, parent, *c, generational, kind),
3586 LuaValue::UserData(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3587 LuaValue::Thread(c) => barrier_gc_child(heap, parent, *c, generational, kind),
3588 LuaValue::Nil
3589 | LuaValue::Bool(_)
3590 | LuaValue::Int(_)
3591 | LuaValue::Float(_)
3592 | LuaValue::LightUserData(_)
3593 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3594 }
3595}
3596
3597fn barrier_gc_child<P, C>(
3598 heap: &lua_gc::Heap,
3599 parent: GcRef<P>,
3600 child: GcRef<C>,
3601 generational: bool,
3602 kind: BarrierKind,
3603)
3604where
3605 P: lua_gc::Trace + 'static,
3606 C: lua_gc::Trace + 'static,
3607{
3608 if generational && matches!(kind, BarrierKind::Forward) {
3609 heap.generational_forward_barrier(parent.0, child.0);
3610 } else if matches!(kind, BarrierKind::Backward) {
3611 heap.barrier_back(parent.0, child.0);
3612 } else {
3613 heap.barrier(parent.0, child.0);
3614 }
3615}
3616
3617fn barrier_child_any<P>(
3618 heap: &lua_gc::Heap,
3619 parent: GcRef<P>,
3620 child: &dyn std::any::Any,
3621 generational: bool,
3622 kind: BarrierKind,
3623)
3624where
3625 P: lua_gc::Trace + 'static,
3626{
3627 if let Some(v) = child.downcast_ref::<LuaValue>() {
3628 barrier_lua_value(heap, parent, v, generational, kind);
3629 } else if let Some(c) = child.downcast_ref::<GcRef<LuaString>>() {
3630 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3631 } else if let Some(c) = child.downcast_ref::<GcRef<LuaTable>>() {
3632 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3633 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureLua>>() {
3634 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3635 } else if let Some(c) = child.downcast_ref::<GcRef<LuaClosureC>>() {
3636 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3637 } else if let Some(c) = child.downcast_ref::<GcRef<LuaUserData>>() {
3638 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3639 } else if let Some(c) = child.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3640 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3641 } else if let Some(c) = child.downcast_ref::<GcRef<LuaProto>>() {
3642 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3643 } else if let Some(c) = child.downcast_ref::<GcRef<UpVal>>() {
3644 barrier_gc_child(heap, parent, c.clone(), generational, kind);
3645 }
3646}
3647
3648fn barrier_any(
3649 heap: &lua_gc::Heap,
3650 parent: &dyn std::any::Any,
3651 child: &dyn std::any::Any,
3652 generational: bool,
3653 kind: BarrierKind,
3654) {
3655 if let Some(v) = parent.downcast_ref::<LuaValue>() {
3656 match v {
3657 LuaValue::Str(p) => barrier_child_any(heap, *p, child, generational, kind),
3658 LuaValue::Table(p) => barrier_child_any(heap, *p, child, generational, kind),
3659 LuaValue::Function(LuaClosure::Lua(p)) => barrier_child_any(heap, *p, child, generational, kind),
3660 LuaValue::Function(LuaClosure::C(p)) => barrier_child_any(heap, *p, child, generational, kind),
3661 LuaValue::UserData(p) => barrier_child_any(heap, *p, child, generational, kind),
3662 LuaValue::Thread(p) => barrier_child_any(heap, *p, child, generational, kind),
3663 LuaValue::Nil
3664 | LuaValue::Bool(_)
3665 | LuaValue::Int(_)
3666 | LuaValue::Float(_)
3667 | LuaValue::LightUserData(_)
3668 | LuaValue::Function(LuaClosure::LightC(_)) => {}
3669 }
3670 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaString>>() {
3671 barrier_child_any(heap, p.clone(), child, generational, kind);
3672 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaTable>>() {
3673 barrier_child_any(heap, p.clone(), child, generational, kind);
3674 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureLua>>() {
3675 barrier_child_any(heap, p.clone(), child, generational, kind);
3676 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaClosureC>>() {
3677 barrier_child_any(heap, p.clone(), child, generational, kind);
3678 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaUserData>>() {
3679 barrier_child_any(heap, p.clone(), child, generational, kind);
3680 } else if let Some(p) = parent.downcast_ref::<GcRef<lua_types::value::LuaThread>>() {
3681 barrier_child_any(heap, p.clone(), child, generational, kind);
3682 } else if let Some(p) = parent.downcast_ref::<GcRef<LuaProto>>() {
3683 barrier_child_any(heap, p.clone(), child, generational, kind);
3684 } else if let Some(p) = parent.downcast_ref::<GcRef<UpVal>>() {
3685 barrier_child_any(heap, p.clone(), child, generational, kind);
3686 }
3687}
3688
3689fn trace_reachable_threads(
3690 global: &GlobalState,
3691 _current_thread_id: u64,
3692 marker: &mut lua_gc::Marker,
3693) {
3694 use lua_gc::Trace;
3695
3696 loop {
3697 let visited_before = marker.visited_count();
3698 for (id, entry) in global.threads.iter() {
3699 if thread_entry_marked_alive(marker, *id, entry) {
3700 if let Ok(thread) = entry.state.try_borrow() {
3701 thread.trace(marker);
3702 }
3703 }
3704 }
3705 marker.drain_gray_queue();
3706 if marker.visited_count() == visited_before {
3707 break;
3708 }
3709 }
3710}
3711
3712fn thread_entry_marked_alive(
3713 marker: &lua_gc::Marker,
3714 id: u64,
3715 entry: &ThreadRegistryEntry,
3716) -> bool {
3717 marker.is_marked_or_old(entry.value.0) && entry.value.id == id
3718}
3719
3720fn lua_value_marked_or_old(marker: &lua_gc::Marker, value: &LuaValue) -> bool {
3721 match value {
3722 LuaValue::Str(v) => marker.is_marked_or_old(v.0),
3723 LuaValue::Table(v) => marker.is_marked_or_old(v.0),
3724 LuaValue::Function(LuaClosure::Lua(v)) => marker.is_marked_or_old(v.0),
3725 LuaValue::Function(LuaClosure::C(v)) => marker.is_marked_or_old(v.0),
3726 LuaValue::UserData(v) => marker.is_marked_or_old(v.0),
3727 LuaValue::Thread(v) => marker.is_marked_or_old(v.0),
3728 LuaValue::Nil
3729 | LuaValue::Bool(_)
3730 | LuaValue::Int(_)
3731 | LuaValue::Float(_)
3732 | LuaValue::LightUserData(_)
3733 | LuaValue::Function(LuaClosure::LightC(_)) => true,
3734 }
3735}
3736
3737fn lua_value_identity(value: &LuaValue) -> Option<usize> {
3738 match value {
3739 LuaValue::Str(v) => Some(v.identity()),
3740 LuaValue::Table(v) => Some(v.identity()),
3741 LuaValue::Function(LuaClosure::Lua(v)) => Some(v.identity()),
3742 LuaValue::Function(LuaClosure::C(v)) => Some(v.identity()),
3743 LuaValue::UserData(v) => Some(v.identity()),
3744 LuaValue::Thread(v) => Some(v.identity()),
3745 LuaValue::Nil
3746 | LuaValue::Bool(_)
3747 | LuaValue::Int(_)
3748 | LuaValue::Float(_)
3749 | LuaValue::LightUserData(_)
3750 | LuaValue::Function(LuaClosure::LightC(_)) => None,
3751 }
3752}
3753
3754fn finalizer_marked_or_old(marker: &lua_gc::Marker, object: &FinalizerObject) -> bool {
3755 match object {
3756 FinalizerObject::Table(t) => marker.is_marked_or_old(t.0),
3757 FinalizerObject::UserData(u) => marker.is_marked_or_old(u.0),
3758 }
3759}
3760
3761fn weak_snapshot_tables<'a>(
3762 snapshot: &'a lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>>,
3763) -> impl Iterator<Item = &'a GcRef<LuaTable>> {
3764 snapshot
3765 .weak_values
3766 .iter()
3767 .chain(snapshot.ephemeron.iter())
3768 .chain(snapshot.all_weak.iter())
3769}
3770
3771fn close_open_upvalues_for_unreachable_threads(
3772 global: &GlobalState,
3773 marker: &mut lua_gc::Marker,
3774) {
3775 use lua_gc::Trace;
3776
3777 let mut closed_values = Vec::<LuaValue>::new();
3778 for (id, entry) in global.threads.iter() {
3779 if entry.value.id != *id {
3780 continue;
3781 }
3782 if thread_entry_marked_alive(marker, *id, entry) {
3783 continue;
3784 }
3785 let Ok(thread) = entry.state.try_borrow() else {
3786 continue;
3787 };
3788 for uv in thread.openupval.iter() {
3789 if !marker.is_visited(uv.identity()) {
3790 continue;
3791 }
3792 let Some((thread_id, idx)) = uv.try_open_payload() else {
3793 continue;
3794 };
3795 if thread_id as u64 != *id {
3796 continue;
3797 }
3798 let value = thread.get_at(idx);
3799 uv.close_with(value.clone());
3800 closed_values.push(value);
3801 }
3802 }
3803 for value in closed_values {
3804 value.trace(marker);
3805 }
3806 marker.drain_gray_queue();
3807}
3808
3809fn record_live_interned_strings(
3810 global: &GlobalState,
3811 marker: &lua_gc::Marker,
3812 live_ids: &std::cell::RefCell<Vec<usize>>,
3813) {
3814 let mut live = live_ids.borrow_mut();
3815 for s in global.interned_lt.values() {
3816 let id = s.identity();
3817 if marker.is_visited(id) {
3818 live.push(id);
3819 }
3820 }
3821}
3822
3823fn retain_live_interned_strings(
3824 global: &mut GlobalState,
3825 mut live_ids: Vec<usize>,
3826) {
3827 if live_ids.is_empty() {
3828 global.interned_lt.clear();
3829 return;
3830 }
3831 if live_ids.len() == global.interned_lt.len() {
3832 return;
3833 }
3834 live_ids.sort_unstable();
3835 live_ids.dedup();
3836 global
3837 .interned_lt
3838 .retain(|_, s| live_ids.binary_search(&s.identity()).is_ok());
3839}
3840
3841impl<'a> GcHandle<'a> {
3842 pub fn check_step(&self) {
3849 if !self._state.global().is_gc_running() {
3850 return;
3851 }
3852 if self._state.global().is_gen_mode() {
3853 let should_collect = {
3854 let g = self._state.global();
3855 g.heap.would_collect() || g.gc_debt() > 0
3856 };
3857 if should_collect {
3858 self.generational_step();
3859 }
3860 } else {
3861 self.collect_via_heap(false);
3862 }
3863 }
3864
3865 pub fn full_collect(&self) {
3867 if self._state.global().is_gen_mode() {
3868 self.fullgen();
3869 } else {
3870 self.collect_via_heap(true);
3871 }
3872 }
3873
3874 fn negative_debt(bytes: usize) -> isize {
3875 -(bytes.min(isize::MAX as usize) as isize)
3876 }
3877
3878 fn set_minor_debt(&self) {
3879 let mut g = self._state.global_mut();
3880 let total = g.total_bytes();
3881 let growth = (total / 100).saturating_mul(g.genminormul as usize);
3882 g.heap
3883 .set_threshold_bytes(total.saturating_add(growth.max(1)));
3884 set_debt(&mut *g, Self::negative_debt(growth));
3885 }
3886
3887 fn set_pause_debt(&self) {
3888 let mut g = self._state.global_mut();
3889 let total = g.total_bytes();
3890 let pause = g.gc_pause_param().max(0) as usize;
3891 let threshold = g.gc_estimate.max(1).saturating_mul(pause) / 100;
3892 let debt = if threshold > total {
3893 Self::negative_debt(threshold - total)
3894 } else {
3895 0
3896 };
3897 let heap_threshold = if threshold > total {
3898 threshold
3899 } else {
3900 total.saturating_add(1)
3901 };
3902 g.heap.set_threshold_bytes(heap_threshold);
3903 set_debt(&mut *g, debt);
3904 }
3905
3906 fn enter_incremental_mode(&self) {
3907 let mut g = self._state.global_mut();
3908 g.heap.reset_all_ages();
3909 g.finalizers.reset_generation_boundaries();
3910 g.gckind = GcKind::Incremental as u8;
3911 g.lastatomic = 0;
3912 }
3913
3914 fn enter_generational_mode(&self) -> usize {
3915 self.collect_via_heap_mode(HeapCollectMode::Full);
3916 let numobjs = {
3917 let mut g = self._state.global_mut();
3918 g.heap.promote_all_to_old();
3919 g.finalizers.promote_all_pending_to_old();
3920 g.heap.allgc_count()
3921 };
3922 let total = self._state.global().total_bytes();
3923 {
3924 let mut g = self._state.global_mut();
3925 g.gckind = GcKind::Generational as u8;
3926 g.lastatomic = 0;
3927 g.gc_estimate = total;
3928 }
3929 self.set_minor_debt();
3930 numobjs
3931 }
3932
3933 fn fullgen(&self) -> usize {
3934 self.enter_incremental_mode();
3935 self.enter_generational_mode()
3936 }
3937
3938 fn stepgenfull(&self, lastatomic: usize) {
3939 if self._state.global().gckind == GcKind::Generational as u8 {
3940 self.enter_incremental_mode();
3941 }
3942 self.collect_via_heap_mode(HeapCollectMode::Full);
3943 let newatomic = self._state.global().heap.allgc_count().max(1);
3944 if newatomic < lastatomic.saturating_add(lastatomic >> 3) {
3945 {
3946 let mut g = self._state.global_mut();
3947 g.heap.promote_all_to_old();
3948 g.finalizers.promote_all_pending_to_old();
3949 }
3950 let total = self._state.global().total_bytes();
3951 {
3952 let mut g = self._state.global_mut();
3953 g.gckind = GcKind::Generational as u8;
3954 g.lastatomic = 0;
3955 g.gc_estimate = total;
3956 }
3957 self.set_minor_debt();
3958 } else {
3959 {
3960 let mut g = self._state.global_mut();
3961 g.heap.reset_all_ages();
3962 g.finalizers.reset_generation_boundaries();
3963 }
3964 let total = self._state.global().total_bytes();
3965 {
3966 let mut g = self._state.global_mut();
3967 g.gckind = GcKind::Incremental as u8;
3968 g.lastatomic = newatomic;
3969 g.gc_estimate = total;
3970 }
3971 self.set_pause_debt();
3972 }
3973 }
3974
3975 fn collect_via_heap(&self, force: bool) {
3984 self.collect_via_heap_mode(if force {
3985 HeapCollectMode::Full
3986 } else {
3987 HeapCollectMode::Step
3988 });
3989 }
3990
3991 fn collect_via_heap_mode(&self, mode: HeapCollectMode) {
3992 use lua_gc::Trace;
3993 let state_ref: &LuaState = &*self._state;
3994
3995 if matches!(mode, HeapCollectMode::Step) {
4001 let g = state_ref.global.borrow();
4002 if !g.heap.would_collect() {
4003 return;
4004 }
4005 }
4006
4007 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4011 let mut g = state_ref.global.borrow_mut();
4012 g.weak_tables_registry.live_snapshot_by_kind()
4013 };
4014
4015 let weak_table_capacity = weak_tables_snapshot.len();
4020 let (pending_snapshot, thread_capacity, interned_capacity): (
4021 Vec<FinalizerObject>,
4022 usize,
4023 usize,
4024 ) = {
4025 let g = state_ref.global.borrow();
4026 let pending = match mode {
4027 HeapCollectMode::Minor => g.finalizers.pending_minor_snapshot(),
4028 HeapCollectMode::Full | HeapCollectMode::Step => g.finalizers.pending_snapshot(),
4029 };
4030 (pending, g.threads.len(), g.interned_lt.len())
4031 };
4032 let finalizer_capacity = pending_snapshot.len();
4033
4034 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4035 std::cell::RefCell::new(std::collections::HashSet::new());
4036 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4037 std::cell::RefCell::new(Vec::new());
4038 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4039 std::cell::RefCell::new(std::collections::HashSet::new());
4040 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4041 std::cell::RefCell::new(std::collections::HashSet::new());
4042 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4043 std::cell::RefCell::new(Vec::new());
4044 let collect_ran = std::cell::Cell::new(false);
4045
4046 {
4047 let global = state_ref.global.borrow();
4048 global.heap.unpause();
4049 let roots = CollectRoots { global: &*global, thread: state_ref };
4050 let hook = |marker: &mut lua_gc::Marker| {
4051 collect_ran.set(true);
4052 alive_ids.borrow_mut().reserve(weak_table_capacity);
4053 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4054 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4055 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4056 live_interned_ids.borrow_mut().reserve(interned_capacity);
4057 trace_reachable_threads(&*global, global.current_thread_id, marker);
4058 close_open_upvalues_for_unreachable_threads(&*global, marker);
4059 loop {
4060 let visited_before = marker.visited_count();
4061 for t in &weak_tables_snapshot.ephemeron {
4062 if !marker.is_marked_or_old(t.0) {
4063 continue;
4064 }
4065 let to_mark = t.ephemeron_values_to_mark_with_value(
4066 &|v| lua_value_marked_or_old(marker, v),
4067 );
4068 for v in &to_mark {
4069 v.trace(marker);
4070 }
4071 }
4072 marker.drain_gray_queue();
4073 if marker.visited_count() == visited_before {
4074 break;
4075 }
4076 }
4077 for pf in &pending_snapshot {
4078 if !finalizer_marked_or_old(marker, pf) {
4079 pf.mark(marker);
4080 finalizing_ids.borrow_mut().insert(pf.identity());
4081 newly_unreachable.borrow_mut().push(pf.clone());
4082 }
4083 }
4084 marker.drain_gray_queue();
4085 loop {
4086 let visited_before = marker.visited_count();
4087 for t in &weak_tables_snapshot.ephemeron {
4088 if !marker.is_marked_or_old(t.0) {
4089 continue;
4090 }
4091 let to_mark = t.ephemeron_values_to_mark_with_value(
4092 &|v| lua_value_marked_or_old(marker, v),
4093 );
4094 for v in &to_mark {
4095 v.trace(marker);
4096 }
4097 }
4098 marker.drain_gray_queue();
4099 if marker.visited_count() == visited_before {
4100 break;
4101 }
4102 }
4103 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4104 let id = t.identity();
4105 if marker.is_marked_or_old(t.0) {
4106 let to_mark = {
4107 let finalizing = finalizing_ids.borrow();
4108 t.prune_weak_dead_with_value(
4109 &|v| lua_value_marked_or_old(marker, v),
4110 &|v| {
4111 lua_value_marked_or_old(marker, v)
4112 && lua_value_identity(v)
4113 .map_or(true, |id| !finalizing.contains(&id))
4114 },
4115 )
4116 };
4117 for v in &to_mark {
4118 v.trace(marker);
4119 }
4120 alive_ids.borrow_mut().insert(id);
4121 }
4122 }
4123 marker.drain_gray_queue();
4124 {
4125 let mut alive = alive_thread_ids.borrow_mut();
4126 for (id, entry) in global.threads.iter() {
4127 if thread_entry_marked_alive(marker, *id, entry) {
4128 alive.insert(*id);
4129 }
4130 }
4131 }
4132 record_live_interned_strings(&*global, marker, &live_interned_ids);
4133 };
4134 match mode {
4135 HeapCollectMode::Full => global.heap.full_collect_with_post_mark(&roots, hook),
4136 HeapCollectMode::Step => global.heap.step_with_post_mark(&roots, hook),
4137 HeapCollectMode::Minor => global.heap.minor_collect_with_post_mark(&roots, hook),
4138 }
4139 }
4140
4141 if !collect_ran.get() {
4142 return;
4143 }
4144
4145 let alive_set = alive_ids.into_inner();
4149 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4150 let alive_thread_ids = alive_thread_ids.into_inner();
4151 let live_interned_ids = live_interned_ids.into_inner();
4152 let mut g = state_ref.global.borrow_mut();
4153 retain_live_interned_strings(&mut *g, live_interned_ids);
4154 g.weak_tables_registry.retain_identities(&alive_set);
4155 let main_thread_id = g.main_thread_id;
4156 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4157 g.cross_thread_upvals
4158 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4159 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4163 for object in &promoted {
4164 if let Some(ptr) = object.heap_ptr() {
4165 g.heap.move_finobj_to_tobefnz(ptr);
4166 }
4167 }
4168 if matches!(mode, HeapCollectMode::Minor) {
4169 g.finalizers.finish_minor_collection();
4170 }
4171 }
4172
4173 pub fn generational_step(&self) -> bool {
4175 self.generational_step_with_major(true)
4176 }
4177
4178 pub fn generational_step_minor_only(&self) -> bool {
4184 self.generational_step_with_major(false)
4185 }
4186
4187 fn generational_step_with_major(&self, allow_major: bool) -> bool {
4188 let (lastatomic, majorbase, majorinc, should_major) = {
4189 let g = self._state.global();
4190 let majorbase = if g.gc_estimate == 0 {
4191 g.total_bytes()
4192 } else {
4193 g.gc_estimate
4194 };
4195 let majormul = g.gc_genmajormul_param().max(0) as usize;
4196 let majorinc = (majorbase / 100).saturating_mul(majormul);
4197 let debt_due = g.gc_debt() > 0 || g.heap.would_collect();
4198 let should_major = allow_major
4199 && debt_due
4200 && g.total_bytes() > majorbase.saturating_add(majorinc);
4201 (g.lastatomic, majorbase, majorinc, should_major)
4202 };
4203
4204 if lastatomic != 0 {
4205 self.stepgenfull(lastatomic);
4206 debug_assert!(self._state.global().is_gen_mode());
4207 return true;
4208 }
4209
4210 if should_major {
4211 let numobjs = self.fullgen();
4212 let after = self._state.global().total_bytes();
4213 if after < majorbase.saturating_add(majorinc / 2) {
4214 self.set_minor_debt();
4215 } else {
4216 {
4217 let mut g = self._state.global_mut();
4218 g.lastatomic = numobjs.max(1);
4219 }
4220 self.set_pause_debt();
4221 }
4222 } else {
4223 self.collect_via_heap_mode(HeapCollectMode::Minor);
4224 self.set_minor_debt();
4225 self._state.global_mut().gc_estimate = majorbase;
4226 }
4227
4228 debug_assert!(self._state.global().is_gen_mode());
4229 true
4230 }
4231
4232 pub fn step(&self) { }
4234
4235 pub fn incremental_step(&self, work_units: isize) -> bool {
4248 self.incremental_step_to_state(work_units, None)
4249 }
4250
4251 pub fn run_until_gc_state_for_test(&self, target: lua_gc::GcState) -> bool {
4256 self.incremental_step_to_state(isize::MAX / 4, Some(target));
4257 self._state.global().heap.gc_state() == target
4258 }
4259
4260 fn incremental_step_to_state(
4261 &self,
4262 work_units: isize,
4263 target: Option<lua_gc::GcState>,
4264 ) -> bool {
4265 use lua_gc::{StepBudget, StepOutcome, Trace};
4266 let state_ref: &LuaState = &*self._state;
4267
4268 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4269 let mut g = state_ref.global.borrow_mut();
4270 g.weak_tables_registry.live_snapshot_by_kind()
4271 };
4272
4273 let weak_table_capacity = weak_tables_snapshot.len();
4274 let (pending_snapshot, thread_capacity, interned_capacity): (
4275 Vec<FinalizerObject>,
4276 usize,
4277 usize,
4278 ) = {
4279 let g = state_ref.global.borrow();
4280 (g.finalizers.pending_snapshot(), g.threads.len(), g.interned_lt.len())
4281 };
4282 let finalizer_capacity = pending_snapshot.len();
4283
4284 let alive_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4285 std::cell::RefCell::new(std::collections::HashSet::new());
4286 let newly_unreachable: std::cell::RefCell<Vec<FinalizerObject>> =
4287 std::cell::RefCell::new(Vec::new());
4288 let finalizing_ids: std::cell::RefCell<std::collections::HashSet<usize>> =
4289 std::cell::RefCell::new(std::collections::HashSet::new());
4290 let alive_thread_ids: std::cell::RefCell<std::collections::HashSet<u64>> =
4291 std::cell::RefCell::new(std::collections::HashSet::new());
4292 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4293 std::cell::RefCell::new(Vec::new());
4294 let atomic_ran = std::cell::Cell::new(false);
4295
4296 let stop_target = {
4297 let g = state_ref.global.borrow();
4298 match (target, g.heap.gc_state()) {
4299 (Some(target), _) => Some(target),
4300 (None, lua_gc::GcState::CallFin) => None,
4301 (None, _) => Some(lua_gc::GcState::CallFin),
4302 }
4303 };
4304
4305 let outcome = {
4306 let global = state_ref.global.borrow();
4307 global.heap.unpause();
4308 let roots = CollectRoots { global: &*global, thread: state_ref };
4309 let hook = |marker: &mut lua_gc::Marker| {
4310 atomic_ran.set(true);
4311 alive_ids.borrow_mut().reserve(weak_table_capacity);
4312 newly_unreachable.borrow_mut().reserve(finalizer_capacity);
4313 finalizing_ids.borrow_mut().reserve(finalizer_capacity);
4314 alive_thread_ids.borrow_mut().reserve(thread_capacity);
4315 live_interned_ids.borrow_mut().reserve(interned_capacity);
4316 trace_reachable_threads(&*global, global.current_thread_id, marker);
4317 close_open_upvalues_for_unreachable_threads(&*global, marker);
4318 loop {
4319 let visited_before = marker.visited_count();
4320 for t in &weak_tables_snapshot.ephemeron {
4321 let t_id = t.identity();
4322 if !marker.is_visited(t_id) {
4323 continue;
4324 }
4325 let to_mark = t.ephemeron_values_to_mark(
4326 &|id| marker.is_visited(id),
4327 );
4328 for v in &to_mark {
4329 v.trace(marker);
4330 }
4331 }
4332 marker.drain_gray_queue();
4333 if marker.visited_count() == visited_before {
4334 break;
4335 }
4336 }
4337 for pf in &pending_snapshot {
4338 if !marker.is_visited(pf.identity()) {
4339 pf.mark(marker);
4340 finalizing_ids.borrow_mut().insert(pf.identity());
4341 newly_unreachable.borrow_mut().push(pf.clone());
4342 }
4343 }
4344 marker.drain_gray_queue();
4345 loop {
4346 let visited_before = marker.visited_count();
4347 for t in &weak_tables_snapshot.ephemeron {
4348 let t_id = t.identity();
4349 if !marker.is_visited(t_id) {
4350 continue;
4351 }
4352 let to_mark = t.ephemeron_values_to_mark(
4353 &|id| marker.is_visited(id),
4354 );
4355 for v in &to_mark {
4356 v.trace(marker);
4357 }
4358 }
4359 marker.drain_gray_queue();
4360 if marker.visited_count() == visited_before {
4361 break;
4362 }
4363 }
4364 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4365 let id = t.identity();
4366 if marker.is_visited(id) {
4367 let to_mark = {
4368 let finalizing = finalizing_ids.borrow();
4369 t.prune_weak_dead_with(
4370 &|id| marker.is_visited(id),
4371 &|id| marker.is_visited(id) && !finalizing.contains(&id),
4372 )
4373 };
4374 for v in &to_mark {
4375 v.trace(marker);
4376 }
4377 alive_ids.borrow_mut().insert(id);
4378 }
4379 }
4380 marker.drain_gray_queue();
4381 {
4382 let mut alive = alive_thread_ids.borrow_mut();
4383 for (id, entry) in global.threads.iter() {
4384 if thread_entry_marked_alive(marker, *id, entry) {
4385 alive.insert(*id);
4386 }
4387 }
4388 }
4389 record_live_interned_strings(&*global, marker, &live_interned_ids);
4390 };
4391 let budget = StepBudget::from_work(work_units);
4392 if let Some(target) = stop_target {
4393 global.heap.incremental_run_until_state_with_post_mark(
4394 &roots,
4395 target,
4396 work_units,
4397 hook,
4398 )
4399 } else {
4400 global.heap.incremental_step_with_post_mark(&roots, budget, hook)
4401 }
4402 };
4403
4404 if atomic_ran.get() {
4405 let alive_set = alive_ids.into_inner();
4406 let promote: Vec<FinalizerObject> = newly_unreachable.into_inner();
4407 let alive_thread_ids = alive_thread_ids.into_inner();
4408 let live_interned_ids = live_interned_ids.into_inner();
4409 let mut g = state_ref.global.borrow_mut();
4410 retain_live_interned_strings(&mut *g, live_interned_ids);
4411 g.weak_tables_registry.retain_identities(&alive_set);
4412 let main_thread_id = g.main_thread_id;
4413 g.threads.retain(|id, _| alive_thread_ids.contains(id));
4414 g.cross_thread_upvals
4415 .retain(|(id, _), _| *id == main_thread_id || alive_thread_ids.contains(id));
4416 let promoted = g.finalizers.promote_pending_to_finalized(promote);
4417 for object in &promoted {
4418 if let Some(ptr) = object.heap_ptr() {
4419 g.heap.move_finobj_to_tobefnz(ptr);
4420 }
4421 }
4422 }
4423
4424 let mut paused = matches!(outcome, StepOutcome::Paused);
4425 if target.is_none()
4426 && self._state.global().heap.gc_state() == lua_gc::GcState::CallFin
4427 && !self._state.global().finalizers.has_to_be_finalized()
4428 {
4429 paused = self._state.global().heap.finish_callfin_phase();
4430 }
4431
4432 paused
4433 }
4434
4435 pub fn prune_weak_tables_mark_only(&self) {
4442 use lua_gc::Trace;
4443 let state_ref: &LuaState = &*self._state;
4444
4445 let weak_tables_snapshot: lua_gc::WeakRegistrySnapshot<GcRef<LuaTable>> = {
4446 let mut g = state_ref.global.borrow_mut();
4447 g.weak_tables_registry.live_snapshot_by_kind()
4448 };
4449 let interned_capacity = {
4450 let g = state_ref.global.borrow();
4451 g.interned_lt.len()
4452 };
4453
4454 let live_interned_ids: std::cell::RefCell<Vec<usize>> =
4455 std::cell::RefCell::new(Vec::new());
4456
4457 {
4458 let global = state_ref.global.borrow();
4459 global.heap.unpause();
4460 let roots = CollectRoots { global: &*global, thread: state_ref };
4461 let hook = |marker: &mut lua_gc::Marker| {
4462 live_interned_ids.borrow_mut().reserve(interned_capacity);
4463 trace_reachable_threads(&*global, global.current_thread_id, marker);
4464 loop {
4465 let visited_before = marker.visited_count();
4466 for t in &weak_tables_snapshot.ephemeron {
4467 let t_id = t.identity();
4468 if !marker.is_visited(t_id) {
4469 continue;
4470 }
4471 let to_mark = t.ephemeron_values_to_mark(
4472 &|id| marker.is_visited(id),
4473 );
4474 for v in &to_mark {
4475 v.trace(marker);
4476 }
4477 }
4478 marker.drain_gray_queue();
4479 if marker.visited_count() == visited_before {
4480 break;
4481 }
4482 }
4483 for t in weak_snapshot_tables(&weak_tables_snapshot) {
4484 if marker.is_visited(t.identity()) {
4485 let to_mark = t.prune_weak_dead(&|id| marker.is_visited(id));
4486 for v in &to_mark {
4487 v.trace(marker);
4488 }
4489 }
4490 }
4491 marker.drain_gray_queue();
4492 record_live_interned_strings(&*global, marker, &live_interned_ids);
4493 };
4494 global.heap.mark_only_with_post_mark(&roots, hook);
4495 }
4496
4497 let live_interned_ids = live_interned_ids.into_inner();
4498 let mut g = state_ref.global.borrow_mut();
4499 retain_live_interned_strings(&mut *g, live_interned_ids);
4500 }
4501
4502 pub fn change_mode(&self, mode: GcKind) {
4504 let old = self._state.global().gckind;
4505 if old == mode as u8 {
4506 self._state.global_mut().lastatomic = 0;
4507 return;
4508 }
4509 match mode {
4510 GcKind::Generational => {
4511 self.enter_generational_mode();
4512 }
4513 GcKind::Incremental => {
4514 self.enter_incremental_mode();
4515 }
4516 }
4517 }
4518
4519 pub fn fix_object<T: lua_gc::Trace + 'static>(&self, _o: &GcRef<T>) { }
4521
4522 pub fn free_all_objects(&self) {
4526 }
4528
4529 pub fn barrier(&self, p: &dyn std::any::Any, v: &LuaValue) {
4533 let g = self._state.global();
4534 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Forward);
4535 }
4536
4537 pub fn barrier_back(&self, p: &dyn std::any::Any, v: &LuaValue) {
4541 let g = self._state.global();
4542 barrier_any(&g.heap, p, v, g.is_gen_mode(), BarrierKind::Backward);
4543 }
4544
4545 pub fn table_barrier_back(&self, p: &GcRef<LuaTable>, v: &LuaValue) {
4547 let g = self._state.global();
4548 barrier_lua_value(&g.heap, *p, v, g.is_gen_mode(), BarrierKind::Backward);
4549 }
4550
4551 pub fn obj_barrier(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4555 let g = self._state.global();
4556 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Forward);
4557 }
4558
4559 pub fn obj_barrier_back(&self, p: &dyn std::any::Any, o: &dyn std::any::Any) {
4562 let g = self._state.global();
4563 barrier_any(&g.heap, p, o, g.is_gen_mode(), BarrierKind::Backward);
4564 }
4565}
4566
4567fn make_seed() -> u32 {
4576 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
4577 {
4578 return crate::string::hash_bytes(b"lua-rs-wasm-seed", 0x9e37_79b9);
4579 }
4580
4581 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
4582 {
4583 use std::time::{SystemTime, UNIX_EPOCH};
4584 let t = SystemTime::now()
4585 .duration_since(UNIX_EPOCH)
4586 .map(|d| d.as_secs() as u32)
4587 .unwrap_or(0);
4588
4589 crate::string::hash_bytes(&t.to_le_bytes(), t)
4597 }
4598}
4599
4600pub(crate) fn set_debt(g: &mut GlobalState, mut debt: isize) {
4614 let tb = g.total_bytes() as isize;
4615 debug_assert!(tb > 0);
4616 if debt < tb.saturating_sub(isize::MAX) {
4618 debt = tb - isize::MAX;
4619 }
4620 g.gc_debt = debt;
4621}
4622
4623pub fn set_c_stack_limit(_state: &mut LuaState, _limit: u32) -> i32 {
4633 let _ = (_state, _limit);
4634 LUAI_MAXCCALLS as i32
4635}
4636
4637pub(crate) fn extend_ci(state: &mut LuaState) -> CallInfoIdx {
4654 debug_assert!(
4655 state.call_info[state.ci.0 as usize].next.is_none(),
4656 "extend_ci: current ci already has a cached next frame"
4657 );
4658
4659 let current_idx = state.ci;
4660 let new_idx = CallInfoIdx(state.call_info.len() as u32);
4662
4663 state.call_info.push(CallInfo {
4664 previous: Some(current_idx),
4665 next: None,
4666 u: CallInfoFrame::lua_default(),
4667 ..CallInfo::default()
4668 });
4669
4670 state.call_info[current_idx.0 as usize].next = Some(new_idx);
4671
4672 state.nci += 1;
4673
4674 new_idx
4675}
4676
4677fn free_ci(state: &mut LuaState) {
4698 let ci_idx = state.ci.0 as usize;
4699
4700 let mut next_opt = state.call_info[ci_idx].next.take();
4701
4702 while let Some(idx) = next_opt {
4703 next_opt = state.call_info[idx.0 as usize].next;
4704 state.nci = state.nci.saturating_sub(1);
4705 }
4706
4707 state.call_info.truncate(ci_idx + 1);
4710}
4711
4712pub(crate) fn shrink_ci(state: &mut LuaState) {
4739 let ci_idx = state.ci.0 as usize;
4740
4741 if state.call_info[ci_idx].next.is_none() {
4742 return;
4743 }
4744
4745 let free_count = state.call_info.len().saturating_sub(ci_idx + 1);
4746 if free_count <= 1 {
4747 return;
4748 }
4749
4750 let keep = free_count / 2;
4754 let removed = free_count - keep;
4755 let new_len = ci_idx + 1 + keep;
4756 state.call_info.truncate(new_len);
4757 state.nci = state.nci.saturating_sub(removed as u32);
4758
4759 if let Some(last) = state.call_info.last_mut() {
4761 last.next = None;
4762 }
4763}
4764
4765pub(crate) fn check_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4777 if state.c_calls() == LUAI_MAXCCALLS {
4780 return Err(LuaError::runtime(format_args!("C stack overflow")));
4781 }
4782 if state.c_calls() >= (LUAI_MAXCCALLS / 10 * 11) {
4784 return Err(LuaError::runtime(format_args!(
4787 "error while handling stack overflow (C stack overflow)"
4788 )));
4789 }
4790 Ok(())
4791}
4792
4793pub fn inc_c_stack(state: &mut LuaState) -> Result<(), LuaError> {
4804 state.n_ccalls += 1;
4805 if state.c_calls() >= LUAI_MAXCCALLS {
4807 check_c_stack(state)?;
4808 }
4809 Ok(())
4810}
4811
4812fn stack_init(thread: &mut LuaState) {
4818 let total_slots = BASIC_STACK_SIZE + EXTRA_STACK;
4820 thread.stack = vec![StackValue::default(); total_slots];
4821
4822 thread.tbclist = Vec::new();
4826
4827 thread.top = StackIdx(0);
4832
4833 thread.stack_last = StackIdx(BASIC_STACK_SIZE as u32);
4834
4835
4836 let base_ci = CallInfo {
4837 func: StackIdx(0),
4838 top: StackIdx(1 + LUA_MINSTACK as u32),
4839 previous: None,
4840 next: None,
4841 callstatus: CIST_C,
4842 nresults: 0,
4843 u: CallInfoFrame::c_default(),
4844 u2: CallInfoExtra::default(),
4845 };
4846
4847 if thread.call_info.is_empty() {
4848 thread.call_info.push(base_ci);
4849 } else {
4850 thread.call_info[0] = base_ci;
4851 thread.call_info.truncate(1);
4852 }
4853
4854 thread.stack[0] = StackValue { val: LuaValue::Nil, tbc_delta: 0 };
4855
4856 thread.top = StackIdx(1);
4857
4858 thread.ci = CallInfoIdx(0);
4859}
4860
4861fn free_stack(state: &mut LuaState) {
4862 if state.stack.is_empty() {
4863 return;
4864 }
4865 state.ci = CallInfoIdx(0);
4866 free_ci(state);
4867 debug_assert_eq!(state.nci, 0, "nci should be 0 after free_ci");
4868 state.stack.clear();
4870 state.stack.shrink_to_fit();
4871}
4872
4873fn init_registry(state: &mut LuaState) -> Result<(), LuaError> {
4874 let registry = state.new_table();
4876
4877 state.global_mut().l_registry = LuaValue::Table(registry.clone());
4879
4880 let globals = state.new_table();
4900 state.global_mut().globals = LuaValue::Table(globals);
4901 let loaded = state.new_table();
4902 state.global_mut().loaded = LuaValue::Table(loaded);
4903
4904 Ok(())
4905}
4906
4907fn lua_open(state: &mut LuaState) -> Result<(), LuaError> {
4908 stack_init(state);
4909 init_registry(state)?;
4910 crate::string::init(state)?;
4911 crate::tagmethods::init(state)?;
4912 state.global_mut().gcstp = 0;
4914 state.global().heap.unpause();
4915 state.global_mut().nilvalue = LuaValue::Nil;
4918 Ok(())
4920}
4921
4922fn preinit_thread(thread: &mut LuaState, global: Rc<RefCell<GlobalState>>) {
4923 thread.global = global;
4924 thread.stack = Vec::new();
4925 thread.call_info = Vec::new();
4926 thread.ci = CallInfoIdx(0);
4929 thread.nci = 0;
4930 thread.n_ccalls = 0;
4934 thread.hook = None;
4935 thread.hookmask = 0;
4936 thread.basehookcount = 0;
4937 thread.allowhook = true;
4938 thread.hookcount = thread.basehookcount;
4940
4941 {
4946 let (active, interval) = {
4947 let g = thread.global.borrow();
4948 (g.sandbox_active(), g.sandbox.interval.get())
4949 };
4950 if active {
4951 thread.hookmask = SANDBOX_COUNT_MASK;
4952 thread.basehookcount = interval;
4953 thread.hookcount = interval;
4954 }
4955 }
4956 thread.openupval = Vec::new();
4957 thread.status = LuaStatus::Ok as u8;
4958 thread.errfunc = 0;
4959 thread.oldpc = 0;
4960 thread.gc_check_needed = true;
4961}
4962
4963fn close_state(state: &mut LuaState) {
4964 let is_complete = state.global().is_complete();
4965
4966 if !is_complete {
4967 state.gc().free_all_objects();
4969 } else {
4970 state.ci = CallInfoIdx(0);
4971 state.gc().free_all_objects();
4974 }
4976
4977 state.global_mut().strt = StringPool::default();
4979
4980 free_stack(state);
4981
4982 }
4987
4988pub fn new_thread(state: &mut LuaState, initial_body: Option<LuaValue>) -> Result<(), LuaError> {
5017 state.gc().check_step();
5018
5019 let global_rc = state.global_rc();
5024 let hookmask = state.hookmask;
5025 let basehookcount = state.basehookcount;
5026
5027 let reserved_id = {
5028 let mut g = state.global_mut();
5029 let id = g.next_thread_id;
5030 g.next_thread_id += 1;
5031 id
5032 };
5033
5034 let mut new_thread = LuaState {
5035 status: LuaStatus::Ok as u8,
5036 allowhook: true,
5037 nci: 0,
5038 top: StackIdx(0),
5039 stack_last: StackIdx(0),
5040 stack: Vec::new(),
5041 ci: CallInfoIdx(0),
5042 call_info: Vec::new(),
5043 openupval: Vec::new(),
5044 tbclist: Vec::new(),
5045 global: global_rc.clone(),
5046 hook: None,
5047 hookmask: 0,
5048 basehookcount: 0,
5049 hookcount: 0,
5050 errfunc: 0,
5051 n_ccalls: 0,
5052 oldpc: 0,
5053 marked: 0,
5054 cached_thread_id: reserved_id,
5055 gc_check_needed: false,
5056 };
5057
5058 preinit_thread(&mut new_thread, global_rc);
5059
5060 new_thread.hookmask = hookmask;
5061 new_thread.basehookcount = basehookcount;
5062 new_thread.reset_hook_count();
5065
5066 stack_init(&mut new_thread);
5072
5073 if let Some(body) = initial_body {
5074 new_thread.push(body);
5075 }
5076
5077 let thread_ref: Rc<RefCell<LuaState>> = Rc::new(RefCell::new(new_thread));
5078
5079 let value = {
5080 let mut g = state.global_mut();
5081 let id = reserved_id;
5082 let value = GcRef::new(lua_types::value::LuaThread::new(id));
5083 g.threads.insert(
5084 id,
5085 ThreadRegistryEntry { state: thread_ref, value: value.clone() },
5086 );
5087 value
5088 };
5089
5090 state.push(LuaValue::Thread(value));
5091
5092 Ok(())
5093}
5094
5095pub fn reset_thread(state: &mut LuaState, status: i32) -> i32 {
5117 state.ci = CallInfoIdx(0);
5118 let ci_idx = 0usize;
5119
5120 if !state.stack.is_empty() {
5122 state.stack[0].val = LuaValue::Nil;
5123 }
5124
5125 state.call_info[ci_idx].func = StackIdx(0);
5126 state.call_info[ci_idx].callstatus = CIST_C;
5127
5128 let mut status = if status == LuaStatus::Yield as i32 {
5129 LuaStatus::Ok as i32
5130 } else {
5131 status
5132 };
5133
5134 state.status = LuaStatus::Ok as u8;
5135
5136 let close_status = crate::do_::close_protected(
5137 state,
5138 StackIdx(1),
5139 LuaStatus::from_raw(status),
5140 );
5141 status = close_status as i32;
5142
5143 if status != LuaStatus::Ok as i32 {
5144 crate::do_::set_error_obj(state, LuaStatus::from_raw(status), StackIdx(1));
5145 } else {
5146 state.top = StackIdx(1);
5147 }
5148
5149 let new_ci_top = StackIdx(state.top.0 + LUA_MINSTACK as u32);
5150 state.call_info[ci_idx].top = new_ci_top;
5151
5152 let needed = new_ci_top.0 as usize;
5155 if state.stack.len() < needed {
5156 state.stack.resize(needed, StackValue::default());
5157 }
5158
5159 status
5160}
5161
5162pub fn close_thread(state: &mut LuaState, from: Option<&LuaState>) -> i32 {
5176 state.n_ccalls = match from {
5178 Some(f) => f.c_calls(),
5179 None => 0,
5180 };
5181 let current_status = state.status as i32;
5182 let result = reset_thread(state, current_status);
5183 result
5184}
5185
5186pub fn reset_thread_api(state: &mut LuaState) -> i32 {
5195 close_thread(state, None)
5196}
5197
5198pub fn new_state() -> Option<LuaState> {
5234 let placeholder_str = GcRef::new(LuaString::placeholder());
5243
5244 let initial_white = 1u8 << WHITE0BIT;
5246
5247 let global = GlobalState {
5251 parser_hook: None,
5252 cli_argv: None,
5253 cli_preload: None,
5254 lua_version: lua_types::LuaVersion::default(),
5255 file_loader_hook: None,
5256 file_open_hook: None,
5257 stdout_hook: None,
5258 stderr_hook: None,
5259 stdin_hook: None,
5260 env_hook: None,
5261 unix_time_hook: None,
5262 cpu_clock_hook: None,
5263 local_offset_hook: None,
5264 entropy_hook: None,
5265 temp_name_hook: None,
5266 popen_hook: None,
5267 file_remove_hook: None,
5268 file_rename_hook: None,
5269 os_execute_hook: None,
5270 dynlib_load_hook: None,
5271 dynlib_symbol_hook: None,
5272 dynlib_unload_hook: None,
5273 sandbox: SandboxLimits::default(),
5274 gc_debt: 0,
5275 gc_estimate: 0,
5276 lastatomic: 0,
5277 strt: StringPool::default(),
5278 l_registry: LuaValue::Nil,
5279 external_roots: ExternalRootSet::default(),
5280 globals: LuaValue::Nil,
5281 loaded: LuaValue::Nil,
5282 nilvalue: LuaValue::Int(0),
5283 seed: make_seed(),
5284 currentwhite: initial_white,
5285 gcstate: GCS_PAUSE,
5286 gckind: GcKind::Incremental as u8,
5288 gcstopem: false,
5289 genminormul: LUAI_GENMINORMUL,
5290 genmajormul: (LUAI_GENMAJORMUL / 4) as u8,
5292 gcstp: GCSTPGC,
5293 gcemergency: false,
5294 gcpause: (LUAI_GCPAUSE / 4) as u8,
5295 gcstepmul: (LUAI_GCMUL / 4) as u8,
5296 gcstepsize: LUAI_GCSTEPSIZE,
5297 gc55_params: [20, 50, 68, 250, 200, 9600],
5300 sweepgc_cursor: 0,
5301 weak_tables_registry: lua_gc::WeakRegistry::default(),
5302 finalizers: lua_gc::FinalizerRegistry::default(),
5303 gc_finalizer_error: None,
5304 twups: Vec::new(),
5305 panic: None,
5306 mainthread: None,
5307 threads: std::collections::HashMap::new(),
5308 main_thread_value: GcRef::new(lua_types::value::LuaThread::new(0)),
5309 current_thread_id: 0,
5310 main_thread_id: 0,
5311 next_thread_id: 1,
5312 memerrmsg: placeholder_str.clone(),
5313 tmname: Vec::new(),
5314 mt: std::array::from_fn(|_| None),
5315 strcache: std::array::from_fn(|_| {
5316 std::array::from_fn(|_| placeholder_str.clone())
5317 }),
5318 interned_lt: InternedStringMap::default(),
5319 warnf: None,
5320 warn_mode: WarnMode::Off,
5321 test_warn_enabled: false,
5322 test_warn_on: false,
5323 test_warn_mode: TestWarnMode::Normal,
5324 test_warn_last_to_cont: false,
5325 test_warn_buffer: Vec::new(),
5326 c_functions: Vec::new(),
5327 heap: lua_gc::Heap::new(),
5328 cross_thread_upvals: std::collections::HashMap::new(),
5329 suspended_parent_stacks: Vec::new(),
5330 suspended_parent_open_upvals: Vec::new(),
5331 };
5332
5333 let global_rc = Rc::new(RefCell::new(global));
5334
5335 let initial_marked = initial_white;
5337
5338 let mut main_thread = LuaState {
5339 status: LuaStatus::Ok as u8,
5340 allowhook: true,
5341 nci: 0,
5342 top: StackIdx(0),
5343 stack_last: StackIdx(0),
5344 stack: Vec::new(),
5345 ci: CallInfoIdx(0),
5346 call_info: Vec::new(),
5347 openupval: Vec::new(),
5348 tbclist: Vec::new(),
5349 global: global_rc.clone(),
5350 hook: None,
5351 hookmask: 0,
5352 basehookcount: 0,
5353 hookcount: 0,
5354 errfunc: 0,
5355 n_ccalls: 0,
5356 oldpc: 0,
5357 marked: initial_marked,
5358 cached_thread_id: 0,
5359 gc_check_needed: false,
5360 };
5361
5362 preinit_thread(&mut main_thread, global_rc.clone());
5363
5364 main_thread.inc_nny();
5366
5367 match lua_open(&mut main_thread) {
5377 Ok(()) => {}
5378 Err(_) => {
5379 close_state(&mut main_thread);
5380 return None;
5381 }
5382 }
5383
5384 Some(main_thread)
5385}
5386
5387pub fn close(mut state: LuaState) {
5403 close_state(&mut state);
5408}
5409
5410pub(crate) fn warning(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5420 let test_warn_enabled = state.global().test_warn_enabled;
5421 if test_warn_enabled {
5422 test_warn(state, msg, to_cont);
5423 return;
5424 }
5425
5426 let has_warnf = state.global().warnf.is_some();
5435 if has_warnf {
5436 let mut warnf = state.global_mut().warnf.take();
5438 if let Some(ref mut f) = warnf {
5439 f(msg, to_cont);
5440 }
5441 state.global_mut().warnf = warnf;
5443 return;
5444 }
5445 default_warn(state, msg, to_cont);
5446}
5447
5448fn test_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5449 let is_control = {
5450 let g = state.global();
5451 !g.test_warn_last_to_cont && !to_cont && msg.first() == Some(&b'@')
5452 };
5453 if is_control {
5454 let mut g = state.global_mut();
5455 match &msg[1..] {
5456 b"off" => g.test_warn_on = false,
5457 b"on" => g.test_warn_on = true,
5458 b"normal" => g.test_warn_mode = TestWarnMode::Normal,
5459 b"allow" => g.test_warn_mode = TestWarnMode::Allow,
5460 b"store" => g.test_warn_mode = TestWarnMode::Store,
5461 _ => {}
5462 }
5463 return;
5464 }
5465
5466 let finished = {
5467 let mut g = state.global_mut();
5468 g.test_warn_last_to_cont = to_cont;
5469 g.test_warn_buffer.extend_from_slice(msg);
5470 if to_cont {
5471 None
5472 } else {
5473 Some((
5474 std::mem::take(&mut g.test_warn_buffer),
5475 g.test_warn_mode,
5476 g.test_warn_on,
5477 ))
5478 }
5479 };
5480
5481 let Some((message, mode, warn_on)) = finished else {
5482 return;
5483 };
5484 match mode {
5485 TestWarnMode::Normal => {
5486 if warn_on && message.first() == Some(&b'#') {
5487 write_warning_message(&message);
5488 }
5489 }
5490 TestWarnMode::Allow => {
5491 if warn_on {
5492 write_warning_message(&message);
5493 }
5494 }
5495 TestWarnMode::Store => {
5496 if let Ok(s) = state.intern_str(&message) {
5497 state.push(LuaValue::Str(s));
5498 let _ = crate::api::set_global(state, b"_WARN");
5499 }
5500 }
5501 }
5502}
5503
5504fn write_warning_message(message: &[u8]) {
5505 use std::io::Write;
5506 let stderr = std::io::stderr();
5507 let mut h = stderr.lock();
5508 let _ = h.write_all(b"Lua warning: ");
5509 let _ = h.write_all(message);
5510 let _ = h.write_all(b"\n");
5511}
5512
5513fn default_warn(state: &mut LuaState, msg: &[u8], to_cont: bool) {
5518 use std::io::Write;
5519 if !to_cont && msg.first() == Some(&b'@') {
5521 match &msg[1..] {
5522 b"off" => state.global_mut().warn_mode = WarnMode::Off,
5523 b"on" => state.global_mut().warn_mode = WarnMode::On,
5524 _ => {}
5525 }
5526 return;
5527 }
5528 let mode = state.global().warn_mode;
5529 match mode {
5530 WarnMode::Off => {}
5531 WarnMode::On | WarnMode::Cont => {
5532 let stderr = std::io::stderr();
5533 let mut h = stderr.lock();
5534 if mode == WarnMode::On {
5535 let _ = h.write_all(b"Lua warning: ");
5536 }
5537 let _ = h.write_all(msg);
5538 if to_cont {
5539 state.global_mut().warn_mode = WarnMode::Cont;
5540 } else {
5541 let _ = h.write_all(b"\n");
5542 state.global_mut().warn_mode = WarnMode::On;
5543 }
5544 }
5545 }
5546}
5547
5548#[cfg(test)]
5549mod tests {
5550 use super::*;
5551
5552 fn test_noop_cclosure(_: &mut LuaState) -> Result<usize, LuaError> {
5553 Ok(0)
5554 }
5555
5556 #[test]
5557 fn external_root_keys_reject_stale_slot_after_reuse() {
5558 let mut roots = ExternalRootSet::default();
5559
5560 let first = roots.insert(LuaValue::Int(1));
5561 assert_eq!(roots.len(), 1);
5562 assert_eq!(roots.get(first), Some(&LuaValue::Int(1)));
5563
5564 assert_eq!(roots.remove(first), Some(LuaValue::Int(1)));
5565 assert!(roots.get(first).is_none());
5566 assert!(roots.remove(first).is_none());
5567 assert_eq!(roots.len(), 0);
5568 assert_eq!(roots.vacant_len(), 1);
5569 assert!(roots.replace(first, LuaValue::Int(9)).is_none());
5570 assert!(roots.is_empty());
5571
5572 let second = roots.insert(LuaValue::Int(2));
5573 assert_eq!(first.index, second.index);
5574 assert_ne!(first, second);
5575 assert!(roots.get(first).is_none());
5576 assert_eq!(roots.get(second), Some(&LuaValue::Int(2)));
5577 assert!(roots.replace(first, LuaValue::Int(3)).is_none());
5578 }
5579
5580 #[test]
5581 fn external_roots_keep_heap_value_alive_until_unrooted() {
5582 let mut state = new_state().expect("state should initialize");
5583 let _heap_guard = {
5584 let g = state.global();
5585 lua_gc::HeapGuard::push(&g.heap)
5586 };
5587
5588 let table = state.new_table();
5589 assert_eq!(state.global().heap.allgc_count(), 1);
5590
5591 let key = state.external_root_value(LuaValue::Table(table));
5592 state.gc().full_collect();
5593 assert_eq!(state.global().heap.allgc_count(), 1);
5594 assert_eq!(state.global().external_roots.len(), 1);
5595
5596 assert!(state.external_unroot_value(key).is_some());
5597 state.gc().full_collect();
5598 assert_eq!(state.global().heap.allgc_count(), 0);
5599 assert!(state.global().external_roots.is_empty());
5600 }
5601
5602 #[test]
5603 fn table_buffer_accounting_refunds_on_sweep() {
5604 let mut state = new_state().expect("state should initialize");
5605 let _heap_guard = {
5606 let g = state.global();
5607 lua_gc::HeapGuard::push(&g.heap)
5608 };
5609
5610 let table = state.new_table();
5611 let key = state.external_root_value(LuaValue::Table(table));
5612 let header_bytes = state.global().heap.bytes_used();
5613 assert!(header_bytes > 0);
5614
5615 for i in 1..=128 {
5616 table
5617 .raw_set_int(&mut state, i, LuaValue::Int(i))
5618 .expect("integer table insert should succeed");
5619 }
5620 let grown_bytes = state.global().heap.bytes_used();
5621 assert!(
5622 grown_bytes > header_bytes,
5623 "table array/hash buffer growth must be charged to the GC heap"
5624 );
5625
5626 state.gc().full_collect();
5627 assert_eq!(
5628 state.global().heap.bytes_used(),
5629 grown_bytes,
5630 "rooted table buffer bytes should remain charged after collection"
5631 );
5632
5633 assert!(state.external_unroot_value(key).is_some());
5634 state.gc().full_collect();
5635 assert_eq!(state.global().heap.bytes_used(), 0);
5636 assert_eq!(state.global().heap.allgc_count(), 0);
5637 }
5638
5639 #[test]
5640 fn userdata_buffer_accounting_refunds_on_sweep() {
5641 let mut state = new_state().expect("state should initialize");
5642 let _heap_guard = {
5643 let g = state.global();
5644 lua_gc::HeapGuard::push(&g.heap)
5645 };
5646
5647 let payload_len = 4096;
5648 let userdata = state
5649 .new_userdata_typed(b"accounting", payload_len, 3)
5650 .expect("userdata allocation should succeed");
5651 state.pop_n(1);
5652 let key = state.external_root_value(LuaValue::UserData(userdata));
5653 let allocated_bytes = state.global().heap.bytes_used();
5654 assert!(
5655 allocated_bytes > payload_len,
5656 "userdata payload bytes must be charged to the GC heap"
5657 );
5658
5659 state.gc().full_collect();
5660 assert_eq!(
5661 state.global().heap.bytes_used(),
5662 allocated_bytes,
5663 "rooted userdata payload bytes should remain charged after collection"
5664 );
5665
5666 assert!(state.external_unroot_value(key).is_some());
5667 state.gc().full_collect();
5668 assert_eq!(state.global().heap.bytes_used(), 0);
5669 assert_eq!(state.global().heap.allgc_count(), 0);
5670 }
5671
5672 #[test]
5673 fn cclosure_upvalue_accounting_refunds_on_sweep() {
5674 let mut state = new_state().expect("state should initialize");
5675 let _heap_guard = {
5676 let g = state.global();
5677 lua_gc::HeapGuard::push(&g.heap)
5678 };
5679
5680 let nupvalues = 64;
5681 for i in 0..nupvalues {
5682 state.push(LuaValue::Int(i as i64));
5683 }
5684 crate::api::push_cclosure(&mut state, test_noop_cclosure, nupvalues as i32)
5685 .expect("C closure creation should succeed");
5686 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
5687 panic!("expected heavy C closure");
5688 };
5689 let expected_payload = ccl.buffer_bytes();
5690 let key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
5691 state.pop_n(1);
5692 let allocated_bytes = state.global().heap.bytes_used();
5693 assert!(
5694 allocated_bytes >= expected_payload,
5695 "C closure upvalue vector bytes must be charged to the GC heap"
5696 );
5697
5698 state.gc().full_collect();
5699 assert_eq!(
5700 state.global().heap.bytes_used(),
5701 allocated_bytes,
5702 "rooted C closure payload bytes should remain charged after collection"
5703 );
5704
5705 assert!(state.external_unroot_value(key).is_some());
5706 state.gc().full_collect();
5707 assert_eq!(state.global().heap.bytes_used(), 0);
5708 assert_eq!(state.global().heap.allgc_count(), 0);
5709 }
5710
5711 #[test]
5712 fn proto_and_lclosure_accounting_refunds_on_sweep() {
5713 let mut state = new_state().expect("state should initialize");
5714 let _heap_guard = {
5715 let g = state.global();
5716 lua_gc::HeapGuard::push(&g.heap)
5717 };
5718
5719 let mut proto = LuaProto::placeholder();
5720 proto.code = vec![lua_types::opcode::Instruction(0); 2048];
5721 proto.lineinfo = vec![0; 2048];
5722 proto.k = vec![LuaValue::Int(1); 512];
5723 let expected_proto_payload = proto.buffer_bytes();
5724 let proto = GcRef::new(proto);
5725 proto.account_buffer(expected_proto_payload as isize);
5726
5727 let closure = state.new_lclosure(proto, 16);
5728 let expected_closure_payload = closure.buffer_bytes();
5729 let key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5730 let allocated_bytes = state.global().heap.bytes_used();
5731 assert!(
5732 allocated_bytes >= expected_proto_payload + expected_closure_payload,
5733 "proto and Lua closure vector bytes must be charged to the GC heap"
5734 );
5735
5736 state.gc().full_collect();
5737 assert_eq!(
5738 state.global().heap.bytes_used(),
5739 allocated_bytes,
5740 "rooted proto and Lua closure payload bytes should remain charged after collection"
5741 );
5742
5743 assert!(state.external_unroot_value(key).is_some());
5744 state.gc().full_collect();
5745 assert_eq!(state.global().heap.bytes_used(), 0);
5746 assert_eq!(state.global().heap.allgc_count(), 0);
5747 }
5748
5749 #[test]
5750 fn string_buffer_accounting_refunds_on_sweep() {
5751 let mut state = new_state().expect("state should initialize");
5752 let _heap_guard = {
5753 let g = state.global();
5754 lua_gc::HeapGuard::push(&g.heap)
5755 };
5756
5757 let payload = vec![b'x'; crate::string::MAX_SHORT_LEN + 4096];
5758 let string = state.intern_str(&payload).expect("long string should allocate");
5759 let key = state.external_root_value(LuaValue::Str(string));
5760 let allocated_bytes = state.global().heap.bytes_used();
5761 assert!(
5762 allocated_bytes > payload.len(),
5763 "long string backing bytes must be charged to the GC heap"
5764 );
5765
5766 state.gc().full_collect();
5767 assert_eq!(
5768 state.global().heap.bytes_used(),
5769 allocated_bytes,
5770 "rooted string buffer bytes should remain charged after collection"
5771 );
5772
5773 assert!(state.external_unroot_value(key).is_some());
5774 state.gc().full_collect();
5775 assert_eq!(state.global().heap.bytes_used(), 0);
5776 assert_eq!(state.global().heap.allgc_count(), 0);
5777 }
5778
5779 #[test]
5780 fn interned_short_string_cache_does_not_root_unreferenced_string() {
5781 let mut state = new_state().expect("state should initialize");
5782 let _heap_guard = {
5783 let g = state.global();
5784 lua_gc::HeapGuard::push(&g.heap)
5785 };
5786
5787 let payload = b"weak-cache-probe-a";
5788 let string = state
5789 .intern_str(payload)
5790 .expect("short string should intern");
5791 let id = string.identity();
5792 assert!(state.global().interned_lt.contains_key(&payload[..]));
5793 assert!(state.global().heap.allocation_token(id).is_some());
5794
5795 state.gc().full_collect();
5796 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5797 assert_eq!(state.global().heap.allocation_token(id), None);
5798 }
5799
5800 #[test]
5801 fn interned_short_string_cache_keeps_reachable_string_until_unrooted() {
5802 let mut state = new_state().expect("state should initialize");
5803 let _heap_guard = {
5804 let g = state.global();
5805 lua_gc::HeapGuard::push(&g.heap)
5806 };
5807
5808 let payload = b"weak-cache-probe-b";
5809 let string = state
5810 .intern_str(payload)
5811 .expect("short string should intern");
5812 let id = string.identity();
5813 let key = state.external_root_value(LuaValue::Str(string));
5814
5815 state.gc().full_collect();
5816 assert!(state.global().interned_lt.contains_key(&payload[..]));
5817 assert!(state.global().heap.allocation_token(id).is_some());
5818
5819 assert!(state.external_unroot_value(key).is_some());
5820 state.gc().full_collect();
5821 assert!(!state.global().interned_lt.contains_key(&payload[..]));
5822 assert_eq!(state.global().heap.allocation_token(id), None);
5823 }
5824
5825 #[test]
5826 fn gc_phase_predicates_follow_heap_state() {
5827 let mut state = new_state().expect("state should initialize");
5828 let _heap_guard = {
5829 let g = state.global();
5830 lua_gc::HeapGuard::push(&g.heap)
5831 };
5832
5833 {
5834 let mut g = state.global_mut();
5835 g.gckind = GcKind::Incremental as u8;
5836 g.lastatomic = 0;
5837 assert!(!g.is_gen_mode());
5838 g.lastatomic = 1;
5839 assert!(g.is_gen_mode());
5840 g.lastatomic = 0;
5841 }
5842
5843 let mut roots = Vec::new();
5844 for _ in 0..16 {
5845 let table = state.new_table();
5846 roots.push(state.external_root_value(LuaValue::Table(table)));
5847 }
5848
5849 let mut saw_keep = false;
5850 let mut saw_sweep = false;
5851 for _ in 0..128 {
5852 state.gc().incremental_step(1);
5853 let g = state.global();
5854 let heap_state = g.heap.gc_state();
5855 assert_eq!(
5856 g.keep_invariant(),
5857 heap_state.is_invariant()
5858 );
5859 assert_eq!(g.is_sweep_phase(), heap_state.is_sweep());
5860 saw_keep |= g.keep_invariant();
5861 saw_sweep |= g.is_sweep_phase();
5862 if heap_state.is_pause() && saw_keep && saw_sweep {
5863 break;
5864 }
5865 }
5866
5867 assert!(saw_keep, "incremental cycle should expose an invariant phase");
5868 assert!(saw_sweep, "incremental cycle should expose a sweep phase");
5869
5870 for key in roots {
5871 assert!(state.external_unroot_value(key).is_some());
5872 }
5873 state.gc().full_collect();
5874 }
5875
5876 #[test]
5877 fn gc_barrier_keeps_new_child_stored_in_black_parent() {
5878 let mut state = new_state().expect("state should initialize");
5879 let _heap_guard = {
5880 let g = state.global();
5881 lua_gc::HeapGuard::push(&g.heap)
5882 };
5883
5884 let parent = state.new_table();
5885 let parent_key = state.external_root_value(LuaValue::Table(parent));
5886 state.gc().incremental_step(1);
5887 assert!(
5888 state.global().keep_invariant(),
5889 "test setup should leave the parent marked during an active cycle"
5890 );
5891
5892 let child = state.new_table();
5893 let parent_value = LuaValue::Table(parent);
5894 let child_value = LuaValue::Table(child);
5895 parent
5896 .raw_set_int(&mut state, 1, child_value)
5897 .expect("table store should succeed");
5898 state.gc_barrier_back(&parent_value, &child_value);
5899
5900 for _ in 0..128 {
5901 if state.gc().incremental_step(1) {
5902 break;
5903 }
5904 }
5905
5906 assert_eq!(state.global().heap.allgc_count(), 2);
5907 assert_eq!(
5908 parent.get_int(1).as_table().map(|t| t.identity()),
5909 Some(child.identity())
5910 );
5911
5912 assert!(state.external_unroot_value(parent_key).is_some());
5913 state.gc().full_collect();
5914 assert_eq!(state.global().heap.allgc_count(), 0);
5915 }
5916
5917 #[test]
5918 fn generational_mode_promotes_and_barriers_age_objects() {
5919 let mut state = new_state().expect("state should initialize");
5920 let _heap_guard = {
5921 let g = state.global();
5922 lua_gc::HeapGuard::push(&g.heap)
5923 };
5924
5925 let parent = state.new_table();
5926 let parent_key = state.external_root_value(LuaValue::Table(parent));
5927
5928 state.gc().change_mode(GcKind::Generational);
5929 assert_eq!(parent.0.age(), lua_gc::GcAge::Old);
5930 assert_eq!(parent.0.color(), lua_gc::Color::Black);
5931 let majorbase = state.global().gc_estimate;
5932 assert!(majorbase > 0);
5933 assert!(state.global().gc_debt() <= 0);
5934
5935 let child = state.new_table();
5936 let parent_value = LuaValue::Table(parent);
5937 let child_value = LuaValue::Table(child);
5938 parent
5939 .raw_set_int(&mut state, 1, child_value.clone())
5940 .expect("table store should succeed");
5941 state.gc_barrier_back(&parent_value, &child_value);
5942 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched1);
5943 assert_eq!(parent.0.color(), lua_gc::Color::Gray);
5944 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5945
5946 let metatable = state.new_table();
5947 parent.set_metatable(Some(metatable));
5948 state.gc().obj_barrier(&parent, &metatable);
5949 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old0);
5950
5951 assert!(state.gc().generational_step_minor_only());
5952 assert_eq!(parent.0.age(), lua_gc::GcAge::Touched2);
5953 assert_eq!(child.0.age(), lua_gc::GcAge::Survival);
5954 assert_eq!(metatable.0.age(), lua_gc::GcAge::Old1);
5955 assert_eq!(state.global().gc_estimate, majorbase);
5956 assert!(state.global().gc_debt() <= 0);
5957
5958 state.gc().change_mode(GcKind::Incremental);
5959 assert_eq!(parent.0.age(), lua_gc::GcAge::New);
5960 assert_eq!(child.0.age(), lua_gc::GcAge::New);
5961 assert_eq!(metatable.0.age(), lua_gc::GcAge::New);
5962
5963 assert!(state.external_unroot_value(parent_key).is_some());
5964 state.gc().full_collect();
5965 }
5966
5967 #[test]
5968 fn generational_upvalue_write_barrier_marks_young_child_old0() {
5969 let mut state = new_state().expect("state should initialize");
5970 let _heap_guard = {
5971 let g = state.global();
5972 lua_gc::HeapGuard::push(&g.heap)
5973 };
5974
5975 let proto = state.new_proto();
5976 let closure = state.new_lclosure(proto, 1);
5977 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
5978 state.gc().change_mode(GcKind::Generational);
5979 let uv = closure.upval(0);
5980 assert_eq!(uv.0.age(), lua_gc::GcAge::Old);
5981
5982 let child = state.new_table();
5983 state
5984 .upvalue_set(&closure, 0, LuaValue::Table(child))
5985 .expect("closed upvalue write should succeed");
5986 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
5987
5988 assert!(state.external_unroot_value(closure_key).is_some());
5989 state.gc().full_collect();
5990 }
5991
5992 #[test]
5993 fn cclosure_setupvalue_replaces_upvalue() {
5994 let mut state = new_state().expect("state should initialize");
5995 let _heap_guard = {
5996 let g = state.global();
5997 lua_gc::HeapGuard::push(&g.heap)
5998 };
5999
6000 let first = state.new_table();
6001 state.push(LuaValue::Table(first));
6002 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6003 .expect("C closure creation should succeed");
6004 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6005 panic!("expected heavy C closure");
6006 };
6007
6008 let second = state.new_table();
6009 state.push(LuaValue::Table(second));
6010 let name = crate::api::setup_value(&mut state, -2, 1)
6011 .expect("C closure upvalue should exist");
6012
6013 assert!(name.is_empty());
6014 let upvalues = ccl.upvalues.borrow();
6015 let LuaValue::Table(actual) = upvalues[0].clone() else {
6016 panic!("expected table upvalue");
6017 };
6018 assert_eq!(actual.identity(), second.identity());
6019 }
6020
6021 #[test]
6022 fn generational_cclosure_setupvalue_barrier_marks_young_child_old0() {
6023 let mut state = new_state().expect("state should initialize");
6024 let _heap_guard = {
6025 let g = state.global();
6026 lua_gc::HeapGuard::push(&g.heap)
6027 };
6028
6029 state.push(LuaValue::Nil);
6030 crate::api::push_cclosure(&mut state, test_noop_cclosure, 1)
6031 .expect("C closure creation should succeed");
6032 let LuaValue::Function(LuaClosure::C(ccl)) = state.get_at(state.top_idx() - 1) else {
6033 panic!("expected heavy C closure");
6034 };
6035 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::C(ccl)));
6036
6037 state.gc().change_mode(GcKind::Generational);
6038 assert_eq!(ccl.0.age(), lua_gc::GcAge::Old);
6039
6040 let child = state.new_table();
6041 state.push(LuaValue::Table(child));
6042 crate::api::setup_value(&mut state, -2, 1)
6043 .expect("C closure upvalue should exist");
6044
6045 assert_eq!(child.0.age(), lua_gc::GcAge::Old0);
6046
6047 assert!(state.external_unroot_value(closure_key).is_some());
6048 state.gc().full_collect();
6049 }
6050
6051 #[test]
6052 fn generational_closure_upvalue_slot_barrier_marks_new_upval_old0() {
6053 let mut state = new_state().expect("state should initialize");
6054 let _heap_guard = {
6055 let g = state.global();
6056 lua_gc::HeapGuard::push(&g.heap)
6057 };
6058
6059 let proto = state.new_proto();
6060 let closure = state.new_lclosure(proto, 1);
6061 let closure_key = state.external_root_value(LuaValue::Function(LuaClosure::Lua(closure)));
6062 state.gc().change_mode(GcKind::Generational);
6063 assert_eq!(closure.0.age(), lua_gc::GcAge::Old);
6064
6065 let replacement = state.new_upval_closed(LuaValue::Nil);
6066 closure.set_upval(0, replacement);
6067 state.gc().obj_barrier(&closure, &replacement);
6068 assert_eq!(replacement.0.age(), lua_gc::GcAge::Old0);
6069
6070 assert!(state.external_unroot_value(closure_key).is_some());
6071 state.gc().full_collect();
6072 }
6073
6074 #[test]
6075 fn cross_thread_upvalue_mirror_traces_values_as_roots() {
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 mirrored = state.new_table();
6083 state
6084 .global_mut()
6085 .cross_thread_upvals
6086 .insert((999, StackIdx(0)), LuaValue::Table(mirrored));
6087
6088 state.gc().full_collect();
6089 assert_eq!(state.global().heap.allgc_count(), 1);
6090
6091 state.global_mut().cross_thread_upvals.clear();
6092 state.gc().full_collect();
6093 assert_eq!(state.global().heap.allgc_count(), 0);
6094 }
6095
6096 #[test]
6097 fn generational_full_collect_promotes_new_survivors_to_old() {
6098 let mut state = new_state().expect("state should initialize");
6099 let _heap_guard = {
6100 let g = state.global();
6101 lua_gc::HeapGuard::push(&g.heap)
6102 };
6103
6104 state.gc().change_mode(GcKind::Generational);
6105 let table = state.new_table();
6106 let table_key = state.external_root_value(LuaValue::Table(table));
6107 assert_eq!(table.0.age(), lua_gc::GcAge::New);
6108
6109 state.gc().full_collect();
6110 assert_eq!(table.0.age(), lua_gc::GcAge::Old);
6111 assert_eq!(table.0.color(), lua_gc::Color::Black);
6112
6113 assert!(state.external_unroot_value(table_key).is_some());
6114 state.gc().full_collect();
6115 }
6116
6117 #[test]
6118 fn gc_packed_params_return_user_visible_values() {
6119 let mut state = new_state().expect("state should initialize");
6120 assert_eq!(
6121 crate::api::gc(&mut state, crate::api::GcArgs::SetPause { value: 200 }),
6122 200
6123 );
6124 assert_eq!(state.global().gc_pause_param(), 200);
6125 assert_eq!(
6126 crate::api::gc(&mut state, crate::api::GcArgs::SetStepMul { value: 200 }),
6127 100
6128 );
6129 assert_eq!(state.global().gc_stepmul_param(), 200);
6130
6131 crate::api::gc(
6132 &mut state,
6133 crate::api::GcArgs::Gen {
6134 minormul: 0,
6135 majormul: 200,
6136 },
6137 );
6138 assert_eq!(state.global().gc_genmajormul_param(), 200);
6139 }
6140
6141 #[test]
6142 fn generational_step_runs_bad_major_when_growth_exceeds_genmajormul() {
6143 let mut state = new_state().expect("state should initialize");
6144 let _heap_guard = {
6145 let g = state.global();
6146 lua_gc::HeapGuard::push(&g.heap)
6147 };
6148
6149 let root = state.new_table();
6150 let root_key = state.external_root_value(LuaValue::Table(root));
6151 state.gc().change_mode(GcKind::Generational);
6152
6153 let root_value = LuaValue::Table(root);
6154 for i in 1..=64 {
6155 let child = state.new_table();
6156 let child_value = LuaValue::Table(child);
6157 root
6158 .raw_set_int(&mut state, i, child_value.clone())
6159 .expect("table store should succeed");
6160 state.gc_barrier_back(&root_value, &child_value);
6161 }
6162
6163 {
6164 let mut g = state.global_mut();
6165 g.gc_estimate = 1;
6166 set_debt(&mut *g, 1);
6167 }
6168
6169 assert!(state.gc().generational_step());
6170 let g = state.global();
6171 assert!(g.is_gen_mode());
6172 assert!(g.lastatomic > 0, "bad major collection should arm stepgenfull");
6173 assert!(g.gc_estimate > 1);
6174 assert!(g.gc_debt() <= 0);
6175 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6176 drop(g);
6177
6178 assert!(state.external_unroot_value(root_key).is_some());
6179 state.gc().full_collect();
6180 }
6181
6182 #[test]
6183 fn generational_implicit_step_runs_major_when_heap_threshold_exceeded() {
6184 let mut state = new_state().expect("state should initialize");
6185 let _heap_guard = {
6186 let g = state.global();
6187 lua_gc::HeapGuard::push(&g.heap)
6188 };
6189
6190 let root = state.new_table();
6191 let root_key = state.external_root_value(LuaValue::Table(root));
6192 state.gc().change_mode(GcKind::Generational);
6193
6194 let root_value = LuaValue::Table(root);
6195 for i in 1..=64 {
6196 let child = state.new_table();
6197 let child_value = LuaValue::Table(child);
6198 root
6199 .raw_set_int(&mut state, i, child_value.clone())
6200 .expect("table store should succeed");
6201 state.gc_barrier_back(&root_value, &child_value);
6202 }
6203
6204 {
6205 let mut g = state.global_mut();
6206 g.gc_estimate = 1;
6207 set_debt(&mut *g, -1);
6208 g.heap.set_threshold_bytes(1);
6209 }
6210
6211 assert!(state.gc().generational_step());
6212 let g = state.global();
6213 assert!(g.is_gen_mode());
6214 assert!(
6215 g.lastatomic > 0,
6216 "implicit threshold-triggered growth should arm a bad major"
6217 );
6218 assert!(g.gc_debt() <= 0);
6219 drop(g);
6220
6221 assert!(state.external_unroot_value(root_key).is_some());
6222 state.gc().full_collect();
6223 }
6224
6225 #[test]
6226 fn generational_stepgenfull_returns_to_gen_after_good_collection() {
6227 let mut state = new_state().expect("state should initialize");
6228 let _heap_guard = {
6229 let g = state.global();
6230 lua_gc::HeapGuard::push(&g.heap)
6231 };
6232
6233 let root = state.new_table();
6234 let root_key = state.external_root_value(LuaValue::Table(root));
6235 state.gc().change_mode(GcKind::Generational);
6236 {
6237 let mut g = state.global_mut();
6238 g.lastatomic = 1024;
6239 }
6240
6241 assert!(state.gc().generational_step());
6242 let g = state.global();
6243 assert_eq!(g.gckind, GcKind::Generational as u8);
6244 assert_eq!(g.lastatomic, 0);
6245 assert!(g.gc_debt() <= 0);
6246 assert_eq!(root.0.age(), lua_gc::GcAge::Old);
6247 assert_eq!(root.0.color(), lua_gc::Color::Black);
6248 drop(g);
6249
6250 assert!(state.external_unroot_value(root_key).is_some());
6251 state.gc().full_collect();
6252 }
6253
6254 #[test]
6255 fn generational_step_zero_reports_false_without_positive_debt() {
6256 let mut state = new_state().expect("state should initialize");
6257 let _heap_guard = {
6258 let g = state.global();
6259 lua_gc::HeapGuard::push(&g.heap)
6260 };
6261
6262 state.gc().change_mode(GcKind::Generational);
6263 assert_eq!(
6264 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 0 }),
6265 0
6266 );
6267 assert_eq!(
6268 crate::api::gc(&mut state, crate::api::GcArgs::Step { data: 1 }),
6269 1
6270 );
6271 }
6272}
6273
6274