1#![allow(dead_code)]
9
10use std::convert::Infallible;
11#[allow(unused_imports)] use crate::prelude::*;
12
13use crate::state::{FinalizerObject, LuaState, LuaCFunction, LuaCallable, StackIdx,
14 LuaValueExt, LuaTypeExt, StackIdxExt,
15 LuaTableRefExt, LuaUserDataRefExt};
16use lua_types::{
17 LuaValue, LuaType, LuaError, LuaString, LuaUserData, LuaClosure,
18 GcRef, LuaStatus,
19};
20use lua_types::value::LuaTable;
21
22pub const LUA_IDENT: &[u8] =
23 b"$LuaVersion: Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio $\
24 $LuaAuthors: R. Ierusalimschy, L. H. Figueiredo, W. Celes $";
25
26const LUA_REGISTRYINDEX: i32 = -(1_000_000) - 1000;
27
28const LUA_MULTRET: i32 = -1;
29
30const LUA_RIDX_GLOBALS: i64 = 2;
31
32const MAX_UPVAL: u8 = 255;
33
34#[inline]
35fn is_pseudo(idx: i32) -> bool {
36 idx <= LUA_REGISTRYINDEX
37}
38
39#[inline]
40fn is_upvalue(idx: i32) -> bool {
41 idx < LUA_REGISTRYINDEX
42}
43
44#[inline]
49fn is_valid_index(state: &LuaState, idx: i32) -> bool {
50 if idx == 0 {
51 return false;
52 }
53 let ci = state.current_call_info();
54 if idx > 0 {
55 let slot = ci.func + idx;
56 slot.0 < state.top_idx().0
57 } else if !is_pseudo(idx) {
58 (-idx) as u32 <= state.top_idx().0.saturating_sub(ci.func.0 + 1)
59 } else if idx == LUA_REGISTRYINDEX {
60 true
61 } else {
62 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
63 let func_val = state.get_at(ci.func);
64 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
65 let upvalues = ccl.upvalues.borrow();
66 upval_n >= 1 && upval_n <= upvalues.len()
67 } else {
68 false
69 }
70 }
71}
72
73fn index_to_value(state: &LuaState, idx: i32) -> LuaValue {
78 let ci = state.current_call_info();
79 if idx > 0 {
80 let func_idx = ci.func;
81 let slot = func_idx + idx;
82 debug_assert!(
83 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
84 "unacceptable index"
85 );
86 if slot.0 >= state.top_idx().0 {
87 LuaValue::Nil
88 } else {
89 state.get_at(slot)
90 }
91 } else if !is_pseudo(idx) {
92 debug_assert!(
94 idx != 0,
95 "invalid index"
96 );
97 let top = state.top_idx();
98 let slot = (top.0 as i32 + idx) as u32;
99 state.get_at(slot)
100 } else if idx == LUA_REGISTRYINDEX {
101 state.registry_value()
102 } else {
103 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
105 debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
106 let func_val = state.get_at(ci.func);
107 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
108 let upvalues = ccl.upvalues.borrow();
110 if upval_n >= 1 && upval_n <= upvalues.len() {
111 upvalues[upval_n - 1].clone()
112 } else {
113 LuaValue::Nil
114 }
115 } else {
116 LuaValue::Nil
117 }
118 }
119}
120
121#[inline]
123fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
124 let ci = state.current_call_info();
125 if idx > 0 {
126 let slot = ci.func + idx;
127 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
128 slot
129 } else {
130 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
131 StackIdx((state.top_idx().0 as i32 + idx) as u32)
132 }
133}
134
135pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
138 debug_assert!(n >= 0, "negative 'n'");
139 let available = state.stack_available();
140 let res = if available > n as usize {
141 true
142 } else {
143 crate::do_::grow_stack(state, n, false).unwrap_or(false)
144 };
145 if res {
146 let needed_top = state.top_idx() + n as i32;
147 let ci_idx = state.current_ci_idx();
148 if state.get_ci(ci_idx).top.0 < needed_top.0 {
149 let live_top = state.top_idx();
150 state.get_ci_mut(ci_idx).top = needed_top;
151 state.clear_stack_range(live_top, needed_top);
152 }
153 }
154 res
155}
156
157pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
174 if n <= 0 {
175 return;
176 }
177 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
178 return;
179 }
180 let abs_top = from.top_idx().0 as i32;
181 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
182 let first_abs = abs_top - n;
183 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
184 for i in 0..n {
185 let idx = StackIdx((first_abs + i) as u32);
186 buf.push(from.get_at(idx));
187 }
188 from.set_top(StackIdx(first_abs as u32));
189 for v in buf {
190 to.push(v);
191 }
192}
193
194pub fn at_panic(
195 state: &mut LuaState,
196 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
197) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
198 let old = state.global_mut().panic;
199 state.global_mut().panic = panicf;
200 old
201}
202
203pub fn version(_state: &LuaState) -> f64 {
204 504.0
205}
206
207pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
208 if idx > 0 || is_pseudo(idx) {
210 idx
211 } else {
212 let ci = state.current_call_info();
213 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
214 }
215}
216
217pub fn get_top(state: &LuaState) -> i32 {
218 let ci = state.current_call_info();
219 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
220}
221
222pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
223 let func = state.current_call_info().func;
224 let ci_top = state.current_call_info().top;
225 if idx >= 0 {
226 debug_assert!(
227 idx as u32 <= ci_top.saturating_sub(func + 1),
228 "new top too large"
229 );
230 let new_top = func + 1 + idx as i32;
231 let old_top = state.top_idx();
232 if new_top.0 > old_top.0 {
233 for i in old_top.0..new_top.0 {
234 state.set_at(i, LuaValue::Nil);
235 }
236 }
237 state.set_top_idx(new_top);
240 } else {
241 debug_assert!(
242 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
243 "invalid new top"
244 );
245 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
246 state.set_top_idx(new_top);
248 }
249 Ok(())
250}
251
252pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
253 let level = index_to_stack_idx(state, idx);
254 state.set_at(level, LuaValue::Nil);
256 Ok(())
257}
258
259#[inline]
260fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
261 let mut lo = from.0;
262 let mut hi = to.0;
263 while lo < hi {
264 let temp = state.get_at(StackIdx(lo));
265 let hi_val = state.get_at(StackIdx(hi));
266 state.set_at(StackIdx(lo), hi_val);
267 state.set_at(StackIdx(hi), temp);
268 lo += 1;
269 hi -= 1;
270 }
271}
272
273pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
274 let t = state.top_idx() - 1;
275 let p = index_to_stack_idx(state, idx);
276 debug_assert!((n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1), "invalid 'n'");
277 let m = if n >= 0 {
278 t - n
279 } else {
280 StackIdx((p.0 as i32 - n - 1) as u32)
281 };
282 reverse_segment(state, p, m);
283 reverse_segment(state, m + 1, t);
284 reverse_segment(state, p, t);
285}
286
287pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
288 let fr = index_to_value(state, fromidx);
289 if is_upvalue(toidx) {
290 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
292 let func_val = state.get_at(state.current_call_info().func);
293 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
294 let wrote = {
295 let mut upvalues = ccl.upvalues.borrow_mut();
296 if upval_n >= 1 && upval_n <= upvalues.len() {
297 upvalues[upval_n - 1] = fr.clone();
298 true
299 } else {
300 false
301 }
302 };
303 if wrote {
304 state.gc().barrier(ccl, &fr);
305 }
306 }
307 } else if toidx == LUA_REGISTRYINDEX {
308 } else {
310 let to_slot = index_to_stack_idx(state, toidx);
311 state.set_at(to_slot, fr);
312 }
313}
314
315pub fn push_value(state: &mut LuaState, idx: i32) {
316 let v = index_to_value(state, idx);
317 state.push(v);
318}
319
320impl LuaState {
325 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
326 push_value(self, idx);
327 Ok(())
328 }
329
330 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
331 push_value(self, idx);
332 Ok(())
333 }
334
335 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
336 rotate(self, idx, 1);
337 Ok(())
338 }
339
340 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
346 len(self, idx)?;
347 let l = match to_integer_x(self, -1) {
348 Some(n) => n,
349 None => {
350 return Err(LuaError::runtime(format_args!(
351 "object length is not an integer"
352 )));
353 }
354 };
355 self.pop_n(1);
356 Ok(l)
357 }
358
359 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
366 if let Some(write_fn) = self.global().stdout_hook {
367 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
368 return Ok(());
369 }
370
371 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
372 {
373 let _ = msg;
374 Err(LuaError::runtime(format_args!(
375 "stdout not available in this host"
376 )))
377 }
378
379 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
380 {
381 use std::io::Write;
382 let stdout = std::io::stdout();
383 let mut handle = stdout.lock();
384 handle
385 .write_all(msg)
386 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
387 handle
388 .flush()
389 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
390 Ok(())
391 }
392 }
393
394 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
403 let abs = abs_index(self, idx);
404 let v = index_to_value(self, abs);
405 let mt: Option<GcRef<LuaTable>> = match &v {
406 LuaValue::Table(t) => t.metatable(),
407 LuaValue::UserData(u) => u.metatable(),
408 _ => self.global().mt[v.base_type() as usize].clone(),
409 };
410 if let Some(mt_ref) = mt {
411 let key = self.intern_str(b"__tostring")?;
412 let f = mt_ref.get_short_str(&key);
413 if !matches!(f, LuaValue::Nil) {
414 let func_idx = self.top_idx();
415 self.push(f);
416 self.push(v.clone());
417 if self.current_ci().is_lua_code() {
418 self.do_call(func_idx, 1)?;
419 } else {
420 self.do_call_no_yield(func_idx, 1)?;
421 }
422 let top = self.top_idx();
423 let result = self.get_at(StackIdx(top.0 - 1));
424 if let LuaValue::Str(s) = result {
425 return Ok(s.as_bytes().to_vec());
426 }
427 return Err(LuaError::runtime(format_args!(
428 "'__tostring' must return a string"
429 )));
430 }
431 }
432 let bytes: Vec<u8> = match &v {
433 LuaValue::Str(s) => {
434 let out = s.as_bytes().to_vec();
435 self.push(LuaValue::Str(s.clone()));
436 out
437 }
438 LuaValue::Int(_) | LuaValue::Float(_) => {
439 let s = crate::object::num_to_string(self, &v)?;
440 let out = s.as_bytes().to_vec();
441 self.push(LuaValue::Str(s));
442 out
443 }
444 LuaValue::Bool(b) => {
445 let lit: &[u8] = if *b { b"true" } else { b"false" };
446 let s = self.intern_str(lit)?;
447 self.push(LuaValue::Str(s));
448 lit.to_vec()
449 }
450 LuaValue::Nil => {
451 let s = self.intern_str(b"nil")?;
452 self.push(LuaValue::Str(s));
453 b"nil".to_vec()
454 }
455 _ => {
456 let kind = crate::tagmethods::obj_type_name(self, &v)?;
457 let ptr = to_pointer(self, abs).unwrap_or(0);
458 let mut buf = kind;
459 buf.extend_from_slice(b": 0x");
460 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
461 let s = self.intern_str(&buf)?;
462 self.push(LuaValue::Str(s));
463 buf
464 }
465 };
466 Ok(bytes)
467 }
468
469 pub fn top(&mut self) -> i32 {
476 get_top(self)
477 }
478
479 pub fn get_top(&mut self) -> i32 {
483 get_top(self)
484 }
485
486 pub fn type_at(&mut self, idx: i32) -> LuaType {
491 lua_type_at(self, idx)
492 }
493
494 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
500 if lua_type_at(self, arg) == LuaType::None {
501 return Err(LuaError::arg_error(arg, "value expected"));
502 }
503 Ok(())
504 }
505
506 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
516 match to_lua_string(self, arg)? {
517 Some(s) => Ok(s.as_bytes().to_vec()),
518 None => {
519 let got = index_to_value(self, arg);
520 let got_name = if lua_type_at(self, arg) == LuaType::None {
521 b"no value".to_vec()
522 } else {
523 crate::tagmethods::obj_type_name(self, &got)?
524 };
525 let extramsg = format!(
526 "string expected, got {}",
527 String::from_utf8_lossy(&got_name)
528 );
529 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
530 }
531 }
532 }
533
534 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
546 match to_integer_x(self, arg) {
547 Some(d) => Ok(d),
548 None => {
549 if is_number(self, arg) {
550 Err(crate::debug::arg_error_impl(
551 self,
552 arg,
553 b"number has no integer representation",
554 ))
555 } else {
556 let got = index_to_value(self, arg);
557 let got_name = if lua_type_at(self, arg) == LuaType::None {
558 b"no value".to_vec()
559 } else {
560 crate::tagmethods::obj_type_name(self, &got)?
561 };
562 let extramsg = format!(
563 "number expected, got {}",
564 String::from_utf8_lossy(&got_name)
565 );
566 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
567 }
568 }
569 }
570 }
571
572 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
582 match to_number_x(self, arg) {
583 Some(d) => Ok(d),
584 None => {
585 let got = index_to_value(self, arg);
586 let got_name = if lua_type_at(self, arg) == LuaType::None {
587 b"no value".to_vec()
588 } else {
589 crate::tagmethods::obj_type_name(self, &got)?
590 };
591 let extramsg = format!(
592 "number expected, got {}",
593 String::from_utf8_lossy(&got_name)
594 );
595 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
596 }
597 }
598 }
599
600 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
610 match lua_type_at(self, arg) {
611 LuaType::None | LuaType::Nil => Ok(def),
612 _ => match to_integer_x(self, arg) {
613 Some(d) => Ok(d),
614 None => {
615 if is_number(self, arg) {
616 Err(LuaError::arg_error(
617 arg,
618 "number has no integer representation",
619 ))
620 } else {
621 let got = index_to_value(self, arg);
622 Err(LuaError::type_arg_error(arg, "number", &got))
623 }
624 }
625 },
626 }
627 }
628
629 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
636 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
637 }
638
639 pub fn protected_call_k(
643 &mut self,
644 nargs: i32,
645 nresults: i32,
646 msgh: i32,
647 ctx: isize,
648 k: Option<crate::state::LuaKFunction>,
649 ) -> Result<(), LuaError> {
650 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
651 }
652
653 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
654 push_lstring(self, s)?;
655 Ok(())
656 }
657
658 pub fn push_c_closure(
659 &mut self,
660 f: fn(&mut LuaState) -> Result<usize, LuaError>,
661 n: i32,
662 ) -> Result<(), LuaError> {
663 push_cclosure(self, f, n)
664 }
665
666 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
667 raw_set_i(self, idx, n)
668 }
669
670 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
671 set_i(self, idx, n)
672 }
673
674 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
678 get_i_value(self, t, n)
679 }
680
681 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
685 set_i_value(self, t, n)
686 }
687
688 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
689 create_table(self, narr, nrec)
690 }
691
692 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
696 set_field(self, LUA_REGISTRYINDEX, key)
697 }
698
699 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
705 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
706 return Ok(false);
707 }
708 self.pop_n(1);
709 create_table(self, 0, 2)?;
710 push_lstring(self, tname)?;
711 set_field(self, -2, b"__name")?;
712 push_value(self, -1);
713 set_field(self, LUA_REGISTRYINDEX, tname)?;
714 Ok(true)
715 }
716
717 pub fn new_lib(
724 &mut self,
725 funcs: &[(&[u8], LuaCFunction)],
726 ) -> Result<(), LuaError> {
727 create_table(self, 0, funcs.len() as i32)?;
728 for (name, f) in funcs {
729 push_cclosure(self, *f, 0)?;
730 set_field(self, -2, name)?;
731 }
732 Ok(())
733 }
734
735 pub fn register_lib(
741 &mut self,
742 _name: &[u8],
743 funcs: &[(&[u8], LuaCFunction)],
744 ) -> Result<(), LuaError> {
745 self.new_lib(funcs)
746 }
747
748 pub fn new_lib_table(
757 &mut self,
758 funcs: &[(&[u8], LuaCFunction)],
759 ) -> Result<(), LuaError> {
760 create_table(self, 0, funcs.len() as i32)
761 }
762
763 pub fn set_funcs_with_upvalues(
768 &mut self,
769 funcs: &[(&[u8], LuaCFunction)],
770 nup: i32,
771 ) -> Result<(), LuaError> {
772 check_stack(self, nup);
773 for (name, f) in funcs {
774 for _ in 0..nup {
775 push_value(self, -nup);
776 }
777 push_cclosure(self, *f, nup)?;
778 set_field(self, -(nup + 2), name)?;
779 }
780 self.pop_n(nup as usize);
781 Ok(())
782 }
783
784 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
785 set_metatable(self, objindex)?;
786 Ok(())
787 }
788
789 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
795 get_field(self, LUA_REGISTRYINDEX, name)?;
796 set_metatable(self, -2)?;
797 Ok(())
798 }
799
800 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
804 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
805 return Ok(true);
806 }
807 self.pop_n(1);
808 let idx = abs_index(self, LUA_REGISTRYINDEX);
809 let new_tbl = self.new_table();
810 self.push(LuaValue::Table(new_tbl));
811 push_value(self, -1);
812 set_field(self, idx, name)?;
813 Ok(false)
814 }
815
816 pub fn new_userdata_typed(
826 &mut self,
827 _name: &[u8],
828 size: usize,
829 nuvalue: i32,
830 ) -> Result<GcRef<LuaUserData>, LuaError> {
831 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
832 let u = GcRef::new(LuaUserData {
834 data: vec![0u8; size].into_boxed_slice(),
835 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
836 metatable: std::cell::RefCell::new(None),
837 host_value: std::cell::RefCell::new(None),
838 });
839 self.push(LuaValue::UserData(u.clone()));
840 self.gc().check_step();
841 Ok(u)
842 }
843}
844
845pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
848 if !is_valid_index(state, idx) {
849 return LuaType::None;
850 }
851 index_to_value(state, idx).base_type()
852}
853
854pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
855 t.type_name()
856}
857
858pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
859 let o = index_to_value(state, idx);
860 matches!(o, LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_)))
861}
862
863pub fn is_integer(state: &LuaState, idx: i32) -> bool {
864 let o = index_to_value(state, idx);
865 matches!(o, LuaValue::Int(_))
866}
867
868pub fn is_number(state: &LuaState, idx: i32) -> bool {
869 let o = index_to_value(state, idx);
870 o.to_number_with_strconv().is_some()
871}
872
873pub fn is_string(state: &LuaState, idx: i32) -> bool {
874 let o = index_to_value(state, idx);
875 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
876}
877
878pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
879 let o = index_to_value(state, idx);
880 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
881}
882
883pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
884 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
885 return false;
886 }
887 let o1 = index_to_value(state, index1);
888 let o2 = index_to_value(state, index2);
889 state.equal_obj(None, &o1, &o2)
890}
891
892pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
894 const LUA_OPUNM: i32 = 12;
897 const LUA_OPBNOT: i32 = 14;
898 if op == LUA_OPUNM || op == LUA_OPBNOT {
899 let top_val = state.get_at(state.top_idx() - 1);
901 state.push(top_val);
902 }
903 let top = state.top_idx();
904 let a = state.get_at(top - 2);
905 let b = state.get_at(top - 1);
906 let result = state.arith_op(op, &a, &b)?;
907 state.set_at(top - 2, result);
908 state.pop();
909 Ok(())
910}
911
912pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
913 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
914 let o1 = index_to_value(state, index1);
915 let o2 = index_to_value(state, index2);
916 if valid {
917 match op {
918 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
919 1 => state.less_than(&o1, &o2),
920 2 => state.less_equal(&o1, &o2),
921 _ => {
922 debug_assert!(false, "invalid option");
923 Ok(false)
924 }
925 }
926 } else {
927 Ok(false)
928 }
929}
930
931pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
932 match state.str_to_num(s) {
934 Some((val, consumed)) => {
935 state.push(val);
936 consumed
937 }
938 None => 0,
939 }
940}
941
942pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
943 let o = index_to_value(state, idx);
944 o.to_number_with_strconv()
945}
946
947pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
948 let o = index_to_value(state, idx);
949 o.to_integer_with_strconv()
950}
951
952pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
953 let o = index_to_value(state, idx);
954 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
955}
956
957pub fn to_lua_string(
959 state: &mut LuaState,
960 idx: i32,
961) -> Result<Option<GcRef<LuaString>>, LuaError> {
962 let o = index_to_value(state, idx);
963 if let LuaValue::Str(s) = &o {
964 return Ok(Some(s.clone()));
965 }
966 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
967 return Ok(None);
968 }
969 state.obj_to_string(idx)?;
970 state.gc().check_step();
971 let updated = index_to_value(state, idx);
972 if let LuaValue::Str(s) = updated {
973 Ok(Some(s))
974 } else {
975 Ok(None)
976 }
977}
978
979pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
980 let o = index_to_value(state, idx);
981 match &o {
982 LuaValue::Str(s) => s.len() as u64,
983 LuaValue::UserData(u) => u.len() as u64,
984 LuaValue::Table(t) => state.table_getn(t) as u64,
985 _ => 0,
986 }
987}
988
989pub fn to_cfunction(
990 state: &LuaState,
991 idx: i32,
992) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
993 let o = index_to_value(state, idx);
994 match o {
995 LuaValue::Function(LuaClosure::LightC(_f)) => None,
999 LuaValue::Function(LuaClosure::C(_ccl)) => None,
1000 _ => None,
1001 }
1002}
1003
1004#[inline]
1005fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1006 match o {
1007 LuaValue::UserData(u) => {
1008 let _ = u;
1012 None
1013 }
1014 LuaValue::LightUserData(p) => Some(*p),
1015 _ => None,
1016 }
1017}
1018
1019pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1020 let o = index_to_value(state, idx);
1021 to_userdata_ptr(&o)
1022}
1023
1024pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1025 let o = index_to_value(state, idx);
1029 if let LuaValue::Thread(t) = o {
1030 Some(t)
1031 } else {
1032 None
1033 }
1034}
1035
1036pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1039 let o = index_to_value(state, idx);
1040 match &o {
1043 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1044 LuaValue::LightUserData(p) => Some(*p as usize),
1045 LuaValue::Str(s) => Some(GcRef::identity(s)),
1046 LuaValue::Table(t) => Some(GcRef::identity(t)),
1047 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1048 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1049 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1050 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1051 _ => None,
1052 }
1053}
1054
1055pub fn push_nil(state: &mut LuaState) {
1058 state.push(LuaValue::Nil);
1059}
1060
1061pub fn push_number(state: &mut LuaState, n: f64) {
1062 state.push(LuaValue::Float(n));
1063}
1064
1065pub fn push_integer(state: &mut LuaState, n: i64) {
1066 state.push(LuaValue::Int(n));
1067}
1068
1069pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1071 let ts = state.intern_str(s)?;
1072 state.push(LuaValue::Str(ts.clone()));
1073 state.gc().check_step();
1074 Ok(ts)
1075}
1076
1077pub fn push_string(state: &mut LuaState, s: Option<&[u8]>) -> Result<Option<GcRef<LuaString>>, LuaError> {
1078 match s {
1079 None => {
1080 state.push(LuaValue::Nil);
1081 state.gc().check_step();
1082 Ok(None)
1083 }
1084 Some(bytes) => {
1085 let ts = state.intern_str(bytes)?;
1086 state.push(LuaValue::Str(ts.clone()));
1087 state.gc().check_step();
1088 Ok(Some(ts))
1089 }
1090 }
1091}
1092
1093pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1097 let ts = state.intern_str(formatted)?;
1098 state.push(LuaValue::Str(ts.clone()));
1099 state.gc().check_step();
1100 Ok(ts)
1101}
1102
1103pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1105 push_vfstring(state, formatted)
1106}
1107
1108pub fn push_cclosure(
1109 state: &mut LuaState,
1110 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1111 n: i32,
1112) -> Result<(), LuaError> {
1113 let idx: lua_types::closure::LuaCFnPtr = {
1127 let mut g = state.global_mut();
1128 if n == 0 {
1129 match g.c_functions.iter().position(|existing| {
1130 existing
1131 .as_bare()
1132 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1133 }) {
1134 Some(i) => i,
1135 None => {
1136 let i = g.c_functions.len();
1137 g.c_functions.push(LuaCallable::bare(f));
1138 i
1139 }
1140 }
1141 } else {
1142 let i = g.c_functions.len();
1143 g.c_functions.push(LuaCallable::bare(f));
1144 i
1145 }
1146 };
1147 if n == 0 {
1148 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1149 } else {
1150 debug_assert!(n > 0 && (n as u32) <= MAX_UPVAL as u32, "upvalue index too large");
1151 let n_usize = n as usize;
1152 let top = state.top_idx();
1153 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1154 let base = top.0 as usize - n_usize;
1155 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1156 for i in 0..n_usize {
1157 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1158 }
1159 state.pop_n(n_usize);
1160 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1162 func: idx,
1163 upvalues: std::cell::RefCell::new(upvalues),
1164 }));
1165 state.push(LuaValue::Function(cl));
1166 state.gc().check_step();
1167 }
1168 Ok(())
1169}
1170
1171pub fn push_boolean(state: &mut LuaState, b: bool) {
1172 state.push(LuaValue::Bool(b));
1173}
1174
1175pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1176 state.push(LuaValue::LightUserData(p));
1177}
1178
1179pub fn push_thread(state: &mut LuaState) -> bool {
1181 let (value, is_main) = {
1182 let g = state.global();
1183 let id = g.current_thread_id;
1184 let v = g
1185 .thread_value_for(id)
1186 .expect("current_thread_id must always resolve to a registered thread");
1187 (v, id == g.main_thread_id)
1188 };
1189 state.push(LuaValue::Thread(value));
1190 is_main
1191}
1192
1193fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1196 let str_val = {
1197 let ts = state.intern_str(k)?;
1198 LuaValue::Str(ts)
1199 };
1200 let result = state.table_get_with_tm(&t, &str_val)?;
1203 state.push(result);
1204 let top = state.top_idx();
1205 Ok(state.get_at(top - 1).base_type())
1206}
1207
1208fn get_global_table(state: &LuaState) -> LuaValue {
1209 state.global().globals.clone()
1215}
1216
1217pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1218 let g = get_global_table(state);
1219 aux_get_str(state, g, name)
1220}
1221
1222pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1223 let t = index_to_value(state, idx);
1224 let top = state.top_idx();
1225 let key = state.get_at(top - 1);
1226 let result = state.table_get_with_tm(&t, &key)?;
1227 state.set_at(top - 1, result);
1228 let val = state.get_at(top - 1);
1229 Ok(val.base_type())
1230}
1231
1232pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1233 let t = index_to_value(state, idx);
1234 aux_get_str(state, t, k)
1235}
1236
1237pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1238 let t = index_to_value(state, idx);
1239 let key = LuaValue::Int(n);
1240 let result = state.table_get_with_tm(&t, &key)?;
1241 state.push(result);
1242 let top = state.top_idx();
1243 Ok(state.get_at(top - 1).base_type())
1244}
1245
1246pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1252 let key = LuaValue::Int(n);
1253 let result = state.table_get_with_tm(t, &key)?;
1254 state.push(result);
1255 let top = state.top_idx();
1256 Ok(state.get_at(top - 1).base_type())
1257}
1258
1259fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1260 let v = val.unwrap_or(LuaValue::Nil);
1261 state.push(v);
1262 let top = state.top_idx();
1263 state.get_at(top - 1).base_type()
1264}
1265
1266fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1267 let t = index_to_value(state, idx);
1268 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1269 if let LuaValue::Table(tbl) = t {
1270 Some(tbl)
1271 } else {
1272 None
1273 }
1274}
1275
1276pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1277 let t = get_table_value(state, idx);
1278 let top = state.top_idx();
1279 let key = state.get_at(top - 1);
1280 let val = t.as_ref().map(|tbl| tbl.get(&key));
1281 state.set_top_idx(top - 1);
1282 finish_raw_get(state, val)
1283}
1284
1285pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1286 let t = get_table_value(state, idx);
1287 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1288 finish_raw_get(state, val)
1289}
1290
1291pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1292 let t = get_table_value(state, idx);
1293 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1294 let val = t.as_ref().map(|tbl| tbl.get(&key));
1295 finish_raw_get(state, val)
1296}
1297
1298pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1299 let t = state.new_table();
1300 if narray > 0 || nrec > 0 {
1301 t.resize(state, narray as usize, nrec as usize)?;
1302 }
1303 state.push(LuaValue::Table(t));
1304 state.gc().check_step();
1305 Ok(())
1306}
1307
1308pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1309 let obj = index_to_value(state, objindex);
1310 let mt: Option<GcRef<LuaTable>> = match &obj {
1311 LuaValue::Table(t) => t.metatable(),
1312 LuaValue::UserData(u) => u.metatable(),
1313 other => {
1314 let idx = other.base_type() as usize;
1315 state.global().mt[idx].clone()
1316 }
1317 };
1318 if let Some(mt_table) = mt {
1319 state.push(LuaValue::Table(mt_table));
1320 true
1321 } else {
1322 false
1323 }
1324}
1325
1326pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1327 let o = index_to_value(state, idx);
1328 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1329 if let LuaValue::UserData(ref u) = o {
1330 let uv = u.uv.borrow();
1331 let uv_count = uv.len() as i32;
1332 if n <= 0 || n > uv_count {
1333 state.push(LuaValue::Nil);
1334 LuaType::None
1335 } else {
1336 let val = uv[(n - 1) as usize].clone();
1337 let t = val.base_type();
1338 state.push(val);
1339 t
1340 }
1341 } else {
1342 state.push(LuaValue::Nil);
1343 LuaType::None
1344 }
1345}
1346
1347fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1350 let str_val = {
1351 let ts = state.intern_str(k)?;
1352 LuaValue::Str(ts)
1353 };
1354 let top = state.top_idx();
1359 let val = state.get_at(top - 1);
1360 state.table_set_with_tm(&t, str_val, val)?;
1361 state.pop();
1362 Ok(())
1363}
1364
1365pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1366 let g = get_global_table(state);
1367 aux_set_str(state, g, name)
1368}
1369
1370pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1371 let t = index_to_value(state, idx);
1372 let top = state.top_idx();
1373 let key = state.get_at(top - 2);
1374 let val = state.get_at(top - 1);
1375 state.table_set_with_tm(&t, key, val)?;
1376 state.set_top_idx(top - 2);
1377 Ok(())
1378}
1379
1380pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1381 let t = index_to_value(state, idx);
1382 aux_set_str(state, t, k)
1383}
1384
1385pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1386 let t = index_to_value(state, idx);
1387 let top = state.top_idx();
1388 let val = state.get_at(top - 1);
1389 let key = LuaValue::Int(n);
1390 state.table_set_with_tm(&t, key, val)?;
1391 state.pop();
1392 Ok(())
1393}
1394
1395pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1401 let top = state.top_idx();
1402 let val = state.get_at(top - 1);
1403 let key = LuaValue::Int(n);
1404 state.table_set_with_tm(t, key, val)?;
1405 state.pop();
1406 Ok(())
1407}
1408
1409fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1410 let t = get_table_value(state, idx)
1411 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1412 let top = state.top_idx();
1413 let val = state.get_at(top - 1);
1414 t.raw_set(state, key, val)?;
1415 t.invalidate_tm_cache();
1416 let top_val = state.get_at(top - 1);
1417 state.gc().barrier_back(&t, &top_val);
1418 state.set_top_idx(top - n as i32);
1419 Ok(())
1420}
1421
1422pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1423 let top = state.top_idx();
1424 let key = state.get_at(top - 2);
1425 aux_raw_set(state, idx, key, 2)
1426}
1427
1428pub fn raw_set_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> Result<(), LuaError> {
1429 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1430 aux_raw_set(state, idx, key, 1)
1431}
1432
1433pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1434 let t = get_table_value(state, idx)
1435 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1436 let top = state.top_idx();
1437 let val = state.get_at(top - 1);
1438 t.raw_set_int(state, n, val)?;
1439 let top_val = state.get_at(top - 1);
1440 state.gc().barrier_back(&t, &top_val);
1441 state.pop();
1442 Ok(())
1443}
1444
1445fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1450 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1451 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1452}
1453
1454fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1455 let id = object.identity();
1456 let already = state
1457 .global()
1458 .pending_finalizers
1459 .iter()
1460 .any(|o| o.identity() == id);
1461 if !already {
1462 state.global_mut().pending_finalizers.push(object);
1463 }
1464}
1465
1466pub fn run_pending_finalizers(state: &mut LuaState) {
1469 let _ = run_pending_finalizers_inner(state, false);
1470}
1471
1472pub fn run_pending_finalizers_inner(
1492 state: &mut LuaState,
1493 propagate: bool,
1494) -> Result<(), LuaError> {
1495 let version = state.global().lua_version;
1496 let mut did_run = false;
1497 loop {
1498 let target_idx = {
1502 let to_fin = &state.global().to_be_finalized;
1503 if to_fin.is_empty() { None } else { Some(to_fin.len() - 1) }
1504 };
1505 let Some(i) = target_idx else { break; };
1506 let object = state.global_mut().to_be_finalized.swap_remove(i);
1516 let mt = object.metatable();
1517 let gc_fn = match mt {
1518 Some(ref m) => {
1519 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1520 m.get_short_str(&name)
1521 }
1522 None => LuaValue::Nil,
1523 };
1524 if !matches!(gc_fn, LuaValue::Function(_)) {
1525 continue;
1526 }
1527 did_run = true;
1528 let saved_top = state.top_idx();
1529 let ci_top = state.current_call_info().top;
1530 if saved_top.0 < ci_top.0 {
1531 state.clear_stack_range(saved_top, ci_top);
1532 state.set_top(ci_top);
1533 }
1534 state.push(gc_fn);
1535 state.push(object.as_lua_value());
1536 let func_idx = state.top_idx() - 2;
1537 let _heap_guard = {
1538 let g = state.global.borrow();
1539 lua_gc::HeapGuard::push(&g.heap)
1540 };
1541 let old_allowhook = state.allowhook;
1542 let old_gcstp = state.global_mut().stop_gc_internal();
1543 state.allowhook = false;
1544 let caller_ci = state.ci;
1545 let caller_status = state.get_ci(caller_ci).callstatus;
1546 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1547 let status = crate::do_::pcall(
1548 state,
1549 |s| s.call_no_yield(func_idx, 0),
1550 func_idx,
1551 0,
1552 );
1553 let finalizer_error: Option<LuaValue> =
1557 if status != LuaStatus::Ok {
1558 Some(state.get_at(state.top_idx() - 1).clone())
1559 } else {
1560 None
1561 };
1562 state.get_ci_mut(caller_ci).callstatus = caller_status;
1563 state.allowhook = old_allowhook;
1564 state.global_mut().set_gc_stop_flags(old_gcstp);
1565 state.set_top(saved_top);
1566
1567 if let Some(errobj) = finalizer_error {
1568 match version {
1569 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1570 let msg: Vec<u8> = match &errobj {
1577 LuaValue::Str(s) => s.as_bytes().to_vec(),
1578 _ => b"no message".to_vec(),
1579 };
1580 let mut wrapped = b"error in __gc metamethod (".to_vec();
1581 wrapped.extend_from_slice(&msg);
1582 wrapped.push(b')');
1583 let interned = state.intern_str(&wrapped)?;
1584 return Err(LuaError::from_value(LuaValue::Str(interned)));
1585 }
1586 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1587 let msg: Vec<u8> = match &errobj {
1591 LuaValue::Str(s) => s.as_bytes().to_vec(),
1592 _ => b"error object is not a string".to_vec(),
1593 };
1594 state.emit_warning(b"error in ", true);
1595 state.emit_warning(b"__gc", true);
1596 state.emit_warning(b" (", true);
1597 state.emit_warning(&msg, true);
1598 state.emit_warning(b")", false);
1599 }
1600 _ => {}
1601 }
1602 }
1603 }
1604 let _ = did_run;
1608 Ok(())
1609}
1610
1611pub fn run_close_finalizers(state: &mut LuaState) {
1630 let pending: Vec<FinalizerObject> = std::mem::take(&mut state.global_mut().pending_finalizers);
1631 if pending.is_empty() {
1632 return;
1633 }
1634 let mut seen = std::collections::HashSet::<usize>::new();
1635 {
1636 let mut g = state.global_mut();
1637 for object in pending {
1638 if seen.insert(object.identity()) {
1639 g.to_be_finalized.push(object);
1640 }
1641 }
1642 }
1643 run_pending_finalizers(state);
1644}
1645
1646fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1652 let mut g = state.global_mut();
1653 g.weak_tables_registry.retain(|w| w.strong_count() > 0);
1654 let mut seen = std::collections::HashSet::<usize>::new();
1655 g.weak_tables_registry
1656 .iter()
1657 .filter_map(|w| w.upgrade())
1658 .filter_map(|rc| {
1659 let id = rc.identity();
1660 if seen.insert(id) {
1661 Some(rc)
1662 } else {
1663 None
1664 }
1665 })
1666 .collect()
1667}
1668
1669pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1670 let top = state.top_idx();
1671 let mt_val = state.get_at(top - 1);
1672 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1673 None
1674 } else {
1675 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1676 if let LuaValue::Table(t) = mt_val {
1677 Some(t)
1678 } else {
1679 None
1680 }
1681 };
1682
1683 let obj = index_to_value(state, objindex);
1684 match obj {
1685 LuaValue::Table(ref tbl) => {
1686 if mt.is_some() {
1687 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1688 }
1689 tbl.set_metatable(mt.clone());
1690 if tbl.weak_mode() != 0 {
1691 state
1692 .global_mut()
1693 .weak_tables_registry
1694 .push(tbl.downgrade());
1695 }
1696 let tables_finalizable =
1706 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1707 if tables_finalizable {
1708 if let Some(ref mt_table) = mt {
1709 if metatable_has_gc(state, mt_table) {
1710 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1711 }
1712 }
1713 }
1714 }
1715 LuaValue::UserData(ref ud) => {
1716 if let Some(ref mt_table) = mt {
1717 state.gc().obj_barrier(ud, mt_table);
1718 if metatable_has_gc(state, mt_table) {
1719 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1720 }
1721 }
1722 ud.set_metatable(mt);
1723 }
1724 ref other => {
1725 let idx = other.base_type() as usize;
1726 state.global_mut().mt[idx] = mt;
1727 }
1728 }
1729 state.pop();
1730 Ok(true)
1731}
1732
1733pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1734 let o = index_to_value(state, idx);
1735 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1736 let top = state.top_idx();
1737 let val = state.get_at(top - 1);
1738 let res = if let LuaValue::UserData(ref ud) = o {
1739 let mut uv = ud.uv.borrow_mut();
1740 let nuvalue = uv.len() as i32;
1741 if n < 1 || n > nuvalue {
1742 false
1743 } else {
1744 uv[(n - 1) as usize] = val.clone();
1745 drop(uv);
1746 state.gc().barrier_back(ud, &val);
1747 true
1748 }
1749 } else {
1750 false
1751 };
1752 state.pop();
1753 Ok(res)
1754}
1755
1756pub fn call_k(
1760 state: &mut LuaState,
1761 nargs: i32,
1762 nresults: i32,
1763 ctx: isize,
1764 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1765) -> Result<(), LuaError> {
1766 let top = state.top_idx();
1767 let func_idx = top - (nargs + 1);
1768 if k.is_some() && state.is_yieldable() {
1774 let ci_idx = state.ci;
1775 {
1776 let ci = state.get_ci_mut(ci_idx);
1777 ci.set_u_c_k(k);
1778 ci.set_u_c_ctx(ctx);
1779 }
1780 state.call_at(func_idx, nresults)?;
1781 } else {
1782 state.call_no_yield(func_idx, nresults)?;
1783 }
1784 state.adjust_results(nresults);
1785 Ok(())
1786}
1787
1788pub fn pcall_k(
1790 state: &mut LuaState,
1791 nargs: i32,
1792 nresults: i32,
1793 errfunc: i32,
1794 ctx: isize,
1795 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1796) -> Result<LuaStatus, LuaError> {
1797 let _heap_guard = {
1802 let g = state.global.borrow();
1803 lua_gc::HeapGuard::push(&g.heap)
1808 };
1809 let err_handler_idx: isize = if errfunc == 0 {
1810 0
1811 } else {
1812 let o = index_to_stack_idx(state, errfunc);
1813 debug_assert!(
1814 matches!(state.get_at(o), LuaValue::Function(_)),
1815 "error handler must be a function"
1816 );
1817 o.0 as isize
1818 };
1819 let top = state.top_idx();
1820 let func_idx = top - (nargs + 1);
1821 if k.is_none() || !state.is_yieldable() {
1822 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
1823 state.adjust_results(nresults);
1824 return Ok(LuaStatus::Ok);
1825 }
1826 let ci_idx = state.ci;
1832 let allow = state.allowhook;
1833 let saved_errfunc = state.errfunc;
1834 {
1835 let ci = state.get_ci_mut(ci_idx);
1836 ci.set_u_c_k(k);
1837 ci.set_u_c_ctx(ctx);
1838 ci.set_u2_funcidx(func_idx.0 as i32);
1839 ci.set_u_c_old_errfunc(saved_errfunc);
1840 ci.set_oah(allow);
1841 ci.callstatus |= crate::state::CIST_YPCALL;
1842 }
1843 state.errfunc = err_handler_idx;
1844 let call_result = crate::do_::call(state, func_idx, nresults);
1845 match call_result {
1846 Ok(()) => {
1847 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
1850 state.errfunc = saved_errfunc;
1851 state.adjust_results(nresults);
1852 Ok(LuaStatus::Ok)
1853 }
1854 Err(crate::state::LuaError::Yield) => {
1855 Err(crate::state::LuaError::Yield)
1859 }
1860 Err(e) => {
1861 Err(e)
1865 }
1866 }
1867}
1868
1869pub fn load(
1873 state: &mut LuaState,
1874 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
1875 chunkname: Option<&[u8]>,
1876 mode: Option<&[u8]>,
1877) -> Result<LuaStatus, LuaError> {
1878 let name = chunkname.unwrap_or(b"?");
1879 let z = crate::zio::ZIO::new(reader);
1880 let status = state.protected_parser(z, name, mode);
1881 if status == LuaStatus::Ok {
1882 let top = state.top_idx();
1883 let func_val = state.get_at(top - 1);
1884 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
1885 if !lcl.upvals.is_empty() {
1886 let gt = get_global_table(state);
1887 let uv = state.new_upval_closed(gt);
1888 lcl.set_upval(0, uv);
1889 state.gc().obj_barrier(&lcl, &uv);
1890 }
1891 }
1892 }
1893 Ok(status)
1894}
1895
1896pub fn dump(
1897 state: &LuaState,
1898 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1899 strip: bool,
1900) -> Result<bool, LuaError> {
1901 let top = state.top_idx();
1902 let o = state.get_at(top - 1);
1903 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1904 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1905 Ok(true)
1906 } else {
1907 Ok(false)
1908 }
1909}
1910
1911pub fn status(state: &LuaState) -> LuaStatus {
1912 LuaStatus::from_raw(state.status as i32)
1913}
1914
1915#[repr(i32)]
1919#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1920pub enum GcWhat {
1921 Stop = 0,
1922 Restart = 1,
1923 Collect = 2,
1924 Count = 3,
1925 CountB = 4,
1926 Step = 5,
1927 SetPause = 6,
1928 SetStepMul = 7,
1929 IsRunning = 9,
1930 Gen = 10,
1931 Inc = 11,
1932}
1933
1934pub enum GcArgs {
1936 Stop,
1937 Restart,
1938 Collect,
1939 Count,
1940 CountB,
1941 Step { data: i32 },
1942 SetPause { value: i32 },
1943 SetStepMul { value: i32 },
1944 IsRunning,
1945 Gen { minormul: i32, majormul: i32 },
1946 Inc { pause: i32, stepmul: i32, stepsize: i32 },
1947 Param { param: usize, value: i64 },
1950}
1951
1952pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
1953 if let GcArgs::Param { param, value } = &args {
1957 return state.global_mut().gc55_param(*param, *value) as i32;
1958 }
1959 if state.global().is_gc_stopped_internally() {
1960 return -1;
1961 }
1962 match args {
1963 GcArgs::Stop => {
1964 state.global_mut().set_gc_stop_user();
1965 }
1966 GcArgs::Restart => {
1967 {
1968 let mut g = state.global_mut();
1969 crate::state::set_debt(&mut *g, 0);
1970 }
1971 state.global_mut().clear_gc_stop();
1972 }
1973 GcArgs::Collect => {
1974 if !state.allowhook {
1975 return 0;
1976 }
1977 state.gc().full_collect();
1983 if let Err(e) = run_pending_finalizers_inner(state, true) {
1991 state.global_mut().gc_finalizer_error = Some(e.into_value());
1992 }
1993 }
1994 GcArgs::Count => {
1995 let g = state.global();
1996 let total = g.heap.bytes_used();
1997 return (total >> 10) as i32;
1998 }
1999 GcArgs::CountB => {
2000 let g = state.global();
2001 let total = g.heap.bytes_used();
2002 return (total & 0x3ff) as i32;
2003 }
2004 GcArgs::Step { data } => {
2005 let old_stp = {
2006 let mut g = state.global_mut();
2007 let old = g.gc_stop_flags();
2008 g.clear_gc_stop();
2009 old
2010 };
2011 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2018 let work_units = if data == 0 {
2019 stepmul
2020 } else {
2021 let raw = (data as isize).saturating_mul(stepmul);
2022 raw.max(1)
2023 };
2024 let debt_for_result = if data == 0 {
2025 let mut g = state.global_mut();
2026 crate::state::set_debt(&mut *g, 0);
2027 0
2028 } else {
2029 let debt = data as isize * 1024 + state.global().gc_debt();
2030 let mut g = state.global_mut();
2031 crate::state::set_debt(&mut *g, debt);
2032 debt
2033 };
2034 let gen_mode = state.global().is_gen_mode();
2035 let cycle_complete = if gen_mode {
2036 state.gc().generational_step();
2037 debt_for_result > 0 && state.global().gc_at_pause()
2038 } else {
2039 state.gc().incremental_step(work_units)
2040 };
2041 state.global_mut().set_gc_stop_flags(old_stp);
2042 {
2044 let heap_state = state.global().heap.gc_state();
2045 let mut g = state.global_mut();
2046 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2047 }
2048 return if cycle_complete { 1 } else { 0 };
2049 }
2050 GcArgs::SetPause { value } => {
2051 let old = state.global().gc_pause_param();
2052 state.global_mut().set_gc_pause_param(value);
2053 return old;
2054 }
2055 GcArgs::SetStepMul { value } => {
2056 let old = state.global().gc_stepmul_param();
2057 state.global_mut().set_gc_stepmul_param(value);
2058 return old;
2059 }
2060 GcArgs::IsRunning => {
2061 return state.global().gc_running() as i32;
2062 }
2063 GcArgs::Gen { minormul, majormul } => {
2064 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2065 if minormul != 0 {
2066 state.global_mut().genminormul = minormul as u8;
2067 }
2068 if majormul != 0 {
2069 state.global_mut().set_gc_genmajormul(majormul);
2070 }
2071 state.gc().change_mode(crate::state::GcKind::Generational);
2072 return old_mode;
2073 }
2074 GcArgs::Inc { pause, stepmul, stepsize } => {
2075 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2076 if pause != 0 {
2077 state.global_mut().set_gc_pause_param(pause);
2078 }
2079 if stepmul != 0 {
2080 state.global_mut().set_gc_stepmul_param(stepmul);
2081 }
2082 if stepsize != 0 {
2083 state.global_mut().gcstepsize = stepsize as u8;
2084 }
2085 state.gc().change_mode(crate::state::GcKind::Incremental);
2086 return old_mode;
2087 }
2088 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2089 }
2090 0
2091}
2092
2093pub fn configure_startup_gc_mode(state: &mut LuaState) {
2100 if matches!(
2101 state.global().lua_version,
2102 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2103 ) {
2104 let _ = gc(state, GcArgs::Restart);
2105 let _ = gc(
2106 state,
2107 GcArgs::Gen {
2108 minormul: 0,
2109 majormul: 0,
2110 },
2111 );
2112 }
2113}
2114
2115pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2122 let top = state.top_idx();
2126 let errobj = state.get_at(top - 1);
2127 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2128 let memerr = state.global().memerrmsg.clone();
2129 GcRef::ptr_eq(s, &memerr)
2130 } else {
2131 false
2132 };
2133 if is_mem_err {
2134 Err(LuaError::Memory)
2135 } else {
2136 Err(LuaError::from_value(errobj))
2137 }
2138}
2139
2140pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2141 let t = get_table_value(state, idx)
2142 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2143 let top = state.top_idx();
2144 let key = state.get_at(top - 1);
2145 match t.next(key)? {
2146 Some((next_key, next_val)) => {
2147 state.set_at(top - 1, next_key);
2148 state.push(next_val);
2149 Ok(true)
2150 }
2151 None => {
2152 state.set_top_idx(top - 1);
2153 Ok(false)
2154 }
2155 }
2156}
2157
2158pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2159 let _level = index_to_stack_idx(state, idx);
2160 Ok(())
2163}
2164
2165pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2166 if n > 0 {
2167 state.concat(n)?;
2168 } else {
2169 let empty = state.intern_str(b"")?;
2170 state.push(LuaValue::Str(empty));
2171 }
2172 state.gc().check_step();
2173 Ok(())
2174}
2175
2176pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2177 let t = index_to_value(state, idx);
2178 let result = state.obj_len(&t)?;
2179 state.push(result);
2180 Ok(())
2181}
2182
2183pub fn set_warn_f(
2188 state: &mut LuaState,
2189 f: Option<Box<dyn FnMut(&[u8], bool)>>,
2190) {
2191 state.global_mut().warnf = f;
2193}
2194
2195pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2196 state.emit_warning(msg, tocont);
2197}
2198
2199pub fn new_userdata_uv(
2200 state: &mut LuaState,
2201 size: usize,
2202 nuvalue: i32,
2203) -> Result<GcRef<LuaUserData>, LuaError> {
2204 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2205 let u = state.new_userdata(size, nuvalue as usize)?;
2206 state.push(LuaValue::UserData(u.clone()));
2207 state.gc().check_step();
2208 Ok(u)
2209}
2210
2211fn aux_upvalue(
2217 state: &LuaState,
2218 fi: &LuaValue,
2219 n: i32,
2220) -> Option<(Vec<u8>, LuaValue)> {
2221 match fi {
2222 LuaValue::Function(LuaClosure::C(ccl)) => {
2223 let upvalues = ccl.upvalues.borrow();
2224 let nupvalues = upvalues.len() as i32;
2225 if n < 1 || n > nupvalues {
2226 return None;
2227 }
2228 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2229 }
2230 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2231 let nupvalues = lcl.upvals.len() as i32;
2232 if n < 1 || n > nupvalues {
2233 return None;
2234 }
2235 let val = state.upvalue_get(lcl, (n - 1) as usize);
2236 let name: Vec<u8> = lcl
2240 .proto
2241 .upvalues
2242 .get((n - 1) as usize)
2243 .and_then(|ud| ud.name.as_ref())
2244 .map(|s| s.as_bytes().to_vec())
2245 .unwrap_or_else(|| b"(no name)".to_vec());
2246 Some((name, val))
2247 }
2248 _ => None,
2249 }
2250}
2251
2252pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2253 let fi = index_to_value(state, funcindex);
2254 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2255 state.push(val);
2256 Some(name)
2257 } else {
2258 None
2259 }
2260}
2261
2262pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2263 let fi = index_to_value(state, funcindex);
2264 let (name, _) = aux_upvalue(state, &fi, n)?;
2265 let new_val = state.pop();
2266 match &fi {
2267 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2268 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2269 }
2270 LuaValue::Function(LuaClosure::C(ccl)) => {
2271 let idx = (n - 1) as usize;
2272 {
2273 let mut upvalues = ccl.upvalues.borrow_mut();
2274 if idx >= upvalues.len() {
2275 return None;
2276 }
2277 upvalues[idx] = new_val.clone();
2278 }
2279 state.gc().barrier(ccl, &new_val);
2280 }
2281 _ => return None,
2282 }
2283 Some(name)
2284}
2285
2286fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2289 let fi = index_to_value(state, fidx);
2290 debug_assert!(matches!(fi, LuaValue::Function(LuaClosure::Lua(_))), "Lua function expected");
2291 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2292 let sizeupvalues = lcl.upvals.len() as i32;
2293 if n >= 1 && n <= sizeupvalues {
2294 Some((n - 1) as usize)
2295 } else {
2296 None
2297 }
2298 } else {
2299 None
2300 }
2301}
2302
2303pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2305 let fi = index_to_value(state, fidx);
2306 match &fi {
2307 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2308 let idx = get_upval_ref_idx(state, fidx, n)?;
2309 Some(GcRef::identity(&lcl.upval(idx)))
2311 }
2312 LuaValue::Function(LuaClosure::C(ccl)) => {
2313 let upvalues = ccl.upvalues.borrow();
2314 if n >= 1 && n <= upvalues.len() as i32 {
2315 Some(GcRef::identity(ccl) ^ (n as usize))
2318 } else {
2319 None
2320 }
2321 }
2322 LuaValue::Function(LuaClosure::LightC(_)) => None,
2323 _ => {
2324 debug_assert!(false, "function expected");
2325 None
2326 }
2327 }
2328}
2329
2330pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2332 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2333 Some(i) => i,
2334 None => return,
2335 };
2336 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2337 Some(i) => i,
2338 None => return,
2339 };
2340 let f1 = index_to_value(state, fidx1);
2341 let f2 = index_to_value(state, fidx2);
2342 if let (
2343 LuaValue::Function(LuaClosure::Lua(lcl1)),
2344 LuaValue::Function(LuaClosure::Lua(lcl2)),
2345 ) = (&f1, &f2)
2346 {
2347 let shared = lcl2.upval(idx2);
2348 lcl1.set_upval(idx1, shared);
2349 state.gc().obj_barrier(lcl1, &shared);
2350 }
2351}
2352
2353