1#![allow(dead_code)]
9
10use std::convert::Infallible;
11#[allow(unused_imports)] use crate::prelude::*;
12
13use crate::state::{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 upval_n >= 1 && upval_n <= ccl.upvalues.len()
66 } else {
67 false
68 }
69 }
70}
71
72fn index_to_value(state: &LuaState, idx: i32) -> LuaValue {
77 let ci = state.current_call_info();
78 if idx > 0 {
79 let func_idx = ci.func;
80 let slot = func_idx + idx;
81 debug_assert!(
82 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
83 "unacceptable index"
84 );
85 if slot.0 >= state.top_idx().0 {
86 LuaValue::Nil
87 } else {
88 state.get_at(slot)
89 }
90 } else if !is_pseudo(idx) {
91 debug_assert!(
93 idx != 0,
94 "invalid index"
95 );
96 let top = state.top_idx();
97 let slot = (top.0 as i32 + idx) as u32;
98 state.get_at(slot)
99 } else if idx == LUA_REGISTRYINDEX {
100 state.registry_value()
101 } else {
102 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
104 debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
105 let func_val = state.get_at(ci.func);
106 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
107 if upval_n >= 1 && upval_n <= ccl.upvalues.len() {
109 ccl.upvalues[upval_n - 1].clone()
110 } else {
111 LuaValue::Nil
112 }
113 } else {
114 LuaValue::Nil
115 }
116 }
117}
118
119#[inline]
121fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
122 let ci = state.current_call_info();
123 if idx > 0 {
124 let slot = ci.func + idx;
125 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
126 slot
127 } else {
128 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
129 StackIdx((state.top_idx().0 as i32 + idx) as u32)
130 }
131}
132
133pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
136 debug_assert!(n >= 0, "negative 'n'");
137 let available = state.stack_available();
138 let res = if available > n as usize {
139 true
140 } else {
141 crate::do_::grow_stack(state, n, false).unwrap_or(false)
142 };
143 if res {
144 let needed_top = state.top_idx() + n as i32;
145 let ci_idx = state.current_ci_idx();
146 if state.get_ci(ci_idx).top.0 < needed_top.0 {
147 let live_top = state.top_idx();
148 state.get_ci_mut(ci_idx).top = needed_top;
149 state.clear_stack_range(live_top, needed_top);
150 }
151 }
152 res
153}
154
155pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
172 if n <= 0 {
173 return;
174 }
175 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
176 return;
177 }
178 let abs_top = from.top_idx().0 as i32;
179 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
180 let first_abs = abs_top - n;
181 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
182 for i in 0..n {
183 let idx = StackIdx((first_abs + i) as u32);
184 buf.push(from.get_at(idx));
185 }
186 from.set_top(StackIdx(first_abs as u32));
187 for v in buf {
188 to.push(v);
189 }
190}
191
192pub fn at_panic(
193 state: &mut LuaState,
194 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
195) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
196 let old = state.global_mut().panic;
197 state.global_mut().panic = panicf;
198 old
199}
200
201pub fn version(_state: &LuaState) -> f64 {
202 504.0
203}
204
205pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
206 if idx > 0 || is_pseudo(idx) {
208 idx
209 } else {
210 let ci = state.current_call_info();
211 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
212 }
213}
214
215pub fn get_top(state: &LuaState) -> i32 {
216 let ci = state.current_call_info();
217 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
218}
219
220pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
221 let func = state.current_call_info().func;
222 let ci_top = state.current_call_info().top;
223 if idx >= 0 {
224 debug_assert!(
225 idx as u32 <= ci_top.saturating_sub(func + 1),
226 "new top too large"
227 );
228 let new_top = func + 1 + idx as i32;
229 let old_top = state.top_idx();
230 if new_top.0 > old_top.0 {
231 for i in old_top.0..new_top.0 {
232 state.set_at(i, LuaValue::Nil);
233 }
234 }
235 state.set_top_idx(new_top);
238 } else {
239 debug_assert!(
240 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
241 "invalid new top"
242 );
243 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
244 state.set_top_idx(new_top);
246 }
247 Ok(())
248}
249
250pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
251 let level = index_to_stack_idx(state, idx);
252 state.set_at(level, LuaValue::Nil);
254 Ok(())
255}
256
257#[inline]
258fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
259 let mut lo = from.0;
260 let mut hi = to.0;
261 while lo < hi {
262 let temp = state.get_at(StackIdx(lo));
263 let hi_val = state.get_at(StackIdx(hi));
264 state.set_at(StackIdx(lo), hi_val);
265 state.set_at(StackIdx(hi), temp);
266 lo += 1;
267 hi -= 1;
268 }
269}
270
271pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
272 let t = state.top_idx() - 1;
273 let p = index_to_stack_idx(state, idx);
274 debug_assert!((n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1), "invalid 'n'");
275 let m = if n >= 0 {
276 t - n
277 } else {
278 StackIdx((p.0 as i32 - n - 1) as u32)
279 };
280 reverse_segment(state, p, m);
281 reverse_segment(state, m + 1, t);
282 reverse_segment(state, p, t);
283}
284
285pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
286 let fr = index_to_value(state, fromidx);
287 if is_upvalue(toidx) {
288 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
290 let func_val = state.get_at(state.current_call_info().func);
291 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
292 let _ = (upval_n, ccl);
295 }
296 } else if toidx == LUA_REGISTRYINDEX {
298 } else {
300 let to_slot = index_to_stack_idx(state, toidx);
301 state.set_at(to_slot, fr);
302 }
303}
304
305pub fn push_value(state: &mut LuaState, idx: i32) {
306 let v = index_to_value(state, idx);
307 state.push(v);
308}
309
310impl LuaState {
315 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
316 push_value(self, idx);
317 Ok(())
318 }
319
320 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
321 push_value(self, idx);
322 Ok(())
323 }
324
325 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
326 rotate(self, idx, 1);
327 Ok(())
328 }
329
330 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
336 len(self, idx)?;
337 let l = match to_integer_x(self, -1) {
338 Some(n) => n,
339 None => {
340 return Err(LuaError::runtime(format_args!(
341 "object length is not an integer"
342 )));
343 }
344 };
345 self.pop_n(1);
346 Ok(l)
347 }
348
349 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
356 if let Some(write_fn) = self.global().stdout_hook {
357 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
358 return Ok(());
359 }
360
361 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
362 {
363 let _ = msg;
364 Err(LuaError::runtime(format_args!(
365 "stdout not available in this host"
366 )))
367 }
368
369 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
370 {
371 use std::io::Write;
372 let stdout = std::io::stdout();
373 let mut handle = stdout.lock();
374 handle
375 .write_all(msg)
376 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
377 handle
378 .flush()
379 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
380 Ok(())
381 }
382 }
383
384 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
393 let abs = abs_index(self, idx);
394 let v = index_to_value(self, abs);
395 let mt: Option<GcRef<LuaTable>> = match &v {
396 LuaValue::Table(t) => t.metatable(),
397 LuaValue::UserData(u) => u.metatable(),
398 _ => self.global().mt[v.base_type() as usize].clone(),
399 };
400 if let Some(mt_ref) = mt {
401 let key = self.intern_str(b"__tostring")?;
402 let f = mt_ref.get_short_str(&key);
403 if !matches!(f, LuaValue::Nil) {
404 let func_idx = self.top_idx();
405 self.push(f);
406 self.push(v.clone());
407 if self.current_ci().is_lua_code() {
408 self.do_call(func_idx, 1)?;
409 } else {
410 self.do_call_no_yield(func_idx, 1)?;
411 }
412 let top = self.top_idx();
413 let result = self.get_at(StackIdx(top.0 - 1));
414 if let LuaValue::Str(s) = result {
415 return Ok(s.as_bytes().to_vec());
416 }
417 return Err(LuaError::runtime(format_args!(
418 "'__tostring' must return a string"
419 )));
420 }
421 }
422 let bytes: Vec<u8> = match &v {
423 LuaValue::Str(s) => {
424 let out = s.as_bytes().to_vec();
425 self.push(LuaValue::Str(s.clone()));
426 out
427 }
428 LuaValue::Int(_) | LuaValue::Float(_) => {
429 let s = crate::object::num_to_string(self, &v)?;
430 let out = s.as_bytes().to_vec();
431 self.push(LuaValue::Str(s));
432 out
433 }
434 LuaValue::Bool(b) => {
435 let lit: &[u8] = if *b { b"true" } else { b"false" };
436 let s = self.intern_str(lit)?;
437 self.push(LuaValue::Str(s));
438 lit.to_vec()
439 }
440 LuaValue::Nil => {
441 let s = self.intern_str(b"nil")?;
442 self.push(LuaValue::Str(s));
443 b"nil".to_vec()
444 }
445 _ => {
446 let kind = crate::tagmethods::obj_type_name(self, &v)?;
447 let ptr = to_pointer(self, abs).unwrap_or(0);
448 let mut buf = kind;
449 buf.extend_from_slice(b": 0x");
450 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
451 let s = self.intern_str(&buf)?;
452 self.push(LuaValue::Str(s));
453 buf
454 }
455 };
456 Ok(bytes)
457 }
458
459 pub fn top(&mut self) -> i32 {
466 get_top(self)
467 }
468
469 pub fn get_top(&mut self) -> i32 {
473 get_top(self)
474 }
475
476 pub fn type_at(&mut self, idx: i32) -> LuaType {
481 lua_type_at(self, idx)
482 }
483
484 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
490 if lua_type_at(self, arg) == LuaType::None {
491 return Err(LuaError::arg_error(arg, "value expected"));
492 }
493 Ok(())
494 }
495
496 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
506 match to_lua_string(self, arg)? {
507 Some(s) => Ok(s.as_bytes().to_vec()),
508 None => {
509 let got = index_to_value(self, arg);
510 let got_name = if lua_type_at(self, arg) == LuaType::None {
511 b"no value".to_vec()
512 } else {
513 crate::tagmethods::obj_type_name(self, &got)?
514 };
515 let extramsg = format!(
516 "string expected, got {}",
517 String::from_utf8_lossy(&got_name)
518 );
519 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
520 }
521 }
522 }
523
524 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
536 match to_integer_x(self, arg) {
537 Some(d) => Ok(d),
538 None => {
539 if is_number(self, arg) {
540 Err(crate::debug::arg_error_impl(
541 self,
542 arg,
543 b"number has no integer representation",
544 ))
545 } else {
546 let got = index_to_value(self, arg);
547 let got_name = if lua_type_at(self, arg) == LuaType::None {
548 b"no value".to_vec()
549 } else {
550 crate::tagmethods::obj_type_name(self, &got)?
551 };
552 let extramsg = format!(
553 "number expected, got {}",
554 String::from_utf8_lossy(&got_name)
555 );
556 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
557 }
558 }
559 }
560 }
561
562 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
572 match to_number_x(self, arg) {
573 Some(d) => Ok(d),
574 None => {
575 let got = index_to_value(self, arg);
576 let got_name = if lua_type_at(self, arg) == LuaType::None {
577 b"no value".to_vec()
578 } else {
579 crate::tagmethods::obj_type_name(self, &got)?
580 };
581 let extramsg = format!(
582 "number expected, got {}",
583 String::from_utf8_lossy(&got_name)
584 );
585 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
586 }
587 }
588 }
589
590 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
600 match lua_type_at(self, arg) {
601 LuaType::None | LuaType::Nil => Ok(def),
602 _ => match to_integer_x(self, arg) {
603 Some(d) => Ok(d),
604 None => {
605 if is_number(self, arg) {
606 Err(LuaError::arg_error(
607 arg,
608 "number has no integer representation",
609 ))
610 } else {
611 let got = index_to_value(self, arg);
612 Err(LuaError::type_arg_error(arg, "number", &got))
613 }
614 }
615 },
616 }
617 }
618
619 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
626 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
627 }
628
629 pub fn protected_call_k(
633 &mut self,
634 nargs: i32,
635 nresults: i32,
636 msgh: i32,
637 ctx: isize,
638 k: Option<crate::state::LuaKFunction>,
639 ) -> Result<(), LuaError> {
640 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
641 }
642
643 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
644 push_lstring(self, s)?;
645 Ok(())
646 }
647
648 pub fn push_c_closure(
649 &mut self,
650 f: fn(&mut LuaState) -> Result<usize, LuaError>,
651 n: i32,
652 ) -> Result<(), LuaError> {
653 push_cclosure(self, f, n)
654 }
655
656 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
657 raw_set_i(self, idx, n)
658 }
659
660 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
661 set_i(self, idx, n)
662 }
663
664 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
668 get_i_value(self, t, n)
669 }
670
671 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
675 set_i_value(self, t, n)
676 }
677
678 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
679 create_table(self, narr, nrec)
680 }
681
682 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
686 set_field(self, LUA_REGISTRYINDEX, key)
687 }
688
689 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
695 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
696 return Ok(false);
697 }
698 self.pop_n(1);
699 create_table(self, 0, 2)?;
700 push_lstring(self, tname)?;
701 set_field(self, -2, b"__name")?;
702 push_value(self, -1);
703 set_field(self, LUA_REGISTRYINDEX, tname)?;
704 Ok(true)
705 }
706
707 pub fn new_lib(
714 &mut self,
715 funcs: &[(&[u8], LuaCFunction)],
716 ) -> Result<(), LuaError> {
717 create_table(self, 0, funcs.len() as i32)?;
718 for (name, f) in funcs {
719 push_cclosure(self, *f, 0)?;
720 set_field(self, -2, name)?;
721 }
722 Ok(())
723 }
724
725 pub fn register_lib(
731 &mut self,
732 _name: &[u8],
733 funcs: &[(&[u8], LuaCFunction)],
734 ) -> Result<(), LuaError> {
735 self.new_lib(funcs)
736 }
737
738 pub fn new_lib_table(
747 &mut self,
748 funcs: &[(&[u8], LuaCFunction)],
749 ) -> Result<(), LuaError> {
750 create_table(self, 0, funcs.len() as i32)
751 }
752
753 pub fn set_funcs_with_upvalues(
758 &mut self,
759 funcs: &[(&[u8], LuaCFunction)],
760 nup: i32,
761 ) -> Result<(), LuaError> {
762 check_stack(self, nup);
763 for (name, f) in funcs {
764 for _ in 0..nup {
765 push_value(self, -nup);
766 }
767 push_cclosure(self, *f, nup)?;
768 set_field(self, -(nup + 2), name)?;
769 }
770 self.pop_n(nup as usize);
771 Ok(())
772 }
773
774 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
775 set_metatable(self, objindex)?;
776 Ok(())
777 }
778
779 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
785 get_field(self, LUA_REGISTRYINDEX, name)?;
786 set_metatable(self, -2)?;
787 Ok(())
788 }
789
790 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
794 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
795 return Ok(true);
796 }
797 self.pop_n(1);
798 let idx = abs_index(self, LUA_REGISTRYINDEX);
799 let new_tbl = self.new_table();
800 self.push(LuaValue::Table(new_tbl));
801 push_value(self, -1);
802 set_field(self, idx, name)?;
803 Ok(false)
804 }
805
806 pub fn new_userdata_typed(
816 &mut self,
817 _name: &[u8],
818 size: usize,
819 nuvalue: i32,
820 ) -> Result<GcRef<LuaUserData>, LuaError> {
821 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
822 let u = GcRef::new(LuaUserData {
824 data: vec![0u8; size].into_boxed_slice(),
825 uv: vec![LuaValue::Nil; nuvalue as usize],
826 metatable: std::cell::RefCell::new(None),
827 host_value: std::cell::RefCell::new(None),
828 });
829 self.push(LuaValue::UserData(u.clone()));
830 self.gc().check_step();
831 Ok(u)
832 }
833}
834
835pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
838 if !is_valid_index(state, idx) {
839 return LuaType::None;
840 }
841 index_to_value(state, idx).base_type()
842}
843
844pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
845 t.type_name()
846}
847
848pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
849 let o = index_to_value(state, idx);
850 matches!(o, LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_)))
851}
852
853pub fn is_integer(state: &LuaState, idx: i32) -> bool {
854 let o = index_to_value(state, idx);
855 matches!(o, LuaValue::Int(_))
856}
857
858pub fn is_number(state: &LuaState, idx: i32) -> bool {
859 let o = index_to_value(state, idx);
860 o.to_number_with_strconv().is_some()
861}
862
863pub fn is_string(state: &LuaState, idx: i32) -> bool {
864 let o = index_to_value(state, idx);
865 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
866}
867
868pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
869 let o = index_to_value(state, idx);
870 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
871}
872
873pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
874 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
875 return false;
876 }
877 let o1 = index_to_value(state, index1);
878 let o2 = index_to_value(state, index2);
879 state.equal_obj(None, &o1, &o2)
880}
881
882pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
884 const LUA_OPUNM: i32 = 12;
887 const LUA_OPBNOT: i32 = 14;
888 if op == LUA_OPUNM || op == LUA_OPBNOT {
889 let top_val = state.get_at(state.top_idx() - 1);
891 state.push(top_val);
892 }
893 let top = state.top_idx();
894 let a = state.get_at(top - 2);
895 let b = state.get_at(top - 1);
896 let result = state.arith_op(op, &a, &b)?;
897 state.set_at(top - 2, result);
898 state.pop();
899 Ok(())
900}
901
902pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
903 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
904 let o1 = index_to_value(state, index1);
905 let o2 = index_to_value(state, index2);
906 if valid {
907 match op {
908 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
909 1 => state.less_than(&o1, &o2),
910 2 => state.less_equal(&o1, &o2),
911 _ => {
912 debug_assert!(false, "invalid option");
913 Ok(false)
914 }
915 }
916 } else {
917 Ok(false)
918 }
919}
920
921pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
922 match state.str_to_num(s) {
924 Some((val, consumed)) => {
925 state.push(val);
926 consumed
927 }
928 None => 0,
929 }
930}
931
932pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
933 let o = index_to_value(state, idx);
934 o.to_number_with_strconv()
935}
936
937pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
938 let o = index_to_value(state, idx);
939 o.to_integer_with_strconv()
940}
941
942pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
943 let o = index_to_value(state, idx);
944 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
945}
946
947pub fn to_lua_string(
949 state: &mut LuaState,
950 idx: i32,
951) -> Result<Option<GcRef<LuaString>>, LuaError> {
952 let o = index_to_value(state, idx);
953 if let LuaValue::Str(s) = &o {
954 return Ok(Some(s.clone()));
955 }
956 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
957 return Ok(None);
958 }
959 state.obj_to_string(idx)?;
960 state.gc().check_step();
961 let updated = index_to_value(state, idx);
962 if let LuaValue::Str(s) = updated {
963 Ok(Some(s))
964 } else {
965 Ok(None)
966 }
967}
968
969pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
970 let o = index_to_value(state, idx);
971 match &o {
972 LuaValue::Str(s) => s.len() as u64,
973 LuaValue::UserData(u) => u.len() as u64,
974 LuaValue::Table(t) => state.table_getn(t) as u64,
975 _ => 0,
976 }
977}
978
979pub fn to_cfunction(
980 state: &LuaState,
981 idx: i32,
982) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
983 let o = index_to_value(state, idx);
984 match o {
985 LuaValue::Function(LuaClosure::LightC(_f)) => None,
989 LuaValue::Function(LuaClosure::C(_ccl)) => None,
990 _ => None,
991 }
992}
993
994#[inline]
995fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
996 match o {
997 LuaValue::UserData(u) => {
998 let _ = u;
1002 None
1003 }
1004 LuaValue::LightUserData(p) => Some(*p),
1005 _ => None,
1006 }
1007}
1008
1009pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1010 let o = index_to_value(state, idx);
1011 to_userdata_ptr(&o)
1012}
1013
1014pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1015 let o = index_to_value(state, idx);
1019 if let LuaValue::Thread(t) = o {
1020 Some(t)
1021 } else {
1022 None
1023 }
1024}
1025
1026pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1029 let o = index_to_value(state, idx);
1030 match &o {
1033 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1034 LuaValue::LightUserData(p) => Some(*p as usize),
1035 LuaValue::Str(s) => Some(GcRef::identity(s)),
1036 LuaValue::Table(t) => Some(GcRef::identity(t)),
1037 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1038 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1039 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1040 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1041 _ => None,
1042 }
1043}
1044
1045pub fn push_nil(state: &mut LuaState) {
1048 state.push(LuaValue::Nil);
1049}
1050
1051pub fn push_number(state: &mut LuaState, n: f64) {
1052 state.push(LuaValue::Float(n));
1053}
1054
1055pub fn push_integer(state: &mut LuaState, n: i64) {
1056 state.push(LuaValue::Int(n));
1057}
1058
1059pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1061 let ts = state.intern_str(s)?;
1062 state.push(LuaValue::Str(ts.clone()));
1063 state.gc().check_step();
1064 Ok(ts)
1065}
1066
1067pub fn push_string(state: &mut LuaState, s: Option<&[u8]>) -> Result<Option<GcRef<LuaString>>, LuaError> {
1068 match s {
1069 None => {
1070 state.push(LuaValue::Nil);
1071 state.gc().check_step();
1072 Ok(None)
1073 }
1074 Some(bytes) => {
1075 let ts = state.intern_str(bytes)?;
1076 state.push(LuaValue::Str(ts.clone()));
1077 state.gc().check_step();
1078 Ok(Some(ts))
1079 }
1080 }
1081}
1082
1083pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1087 let ts = state.intern_str(formatted)?;
1088 state.push(LuaValue::Str(ts.clone()));
1089 state.gc().check_step();
1090 Ok(ts)
1091}
1092
1093pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1095 push_vfstring(state, formatted)
1096}
1097
1098pub fn push_cclosure(
1099 state: &mut LuaState,
1100 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1101 n: i32,
1102) -> Result<(), LuaError> {
1103 let idx: lua_types::closure::LuaCFnPtr = {
1117 let mut g = state.global_mut();
1118 if n == 0 {
1119 match g.c_functions.iter().position(|existing| {
1120 existing
1121 .as_bare()
1122 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1123 }) {
1124 Some(i) => i,
1125 None => {
1126 let i = g.c_functions.len();
1127 g.c_functions.push(LuaCallable::bare(f));
1128 i
1129 }
1130 }
1131 } else {
1132 let i = g.c_functions.len();
1133 g.c_functions.push(LuaCallable::bare(f));
1134 i
1135 }
1136 };
1137 if n == 0 {
1138 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1139 } else {
1140 debug_assert!(n > 0 && (n as u32) <= MAX_UPVAL as u32, "upvalue index too large");
1141 let n_usize = n as usize;
1142 let top = state.top_idx();
1143 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1144 let base = top.0 as usize - n_usize;
1145 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1146 for i in 0..n_usize {
1147 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1148 }
1149 state.pop_n(n_usize);
1150 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1152 func: idx,
1153 upvalues,
1154 }));
1155 state.push(LuaValue::Function(cl));
1156 state.gc().check_step();
1157 }
1158 Ok(())
1159}
1160
1161pub fn push_boolean(state: &mut LuaState, b: bool) {
1162 state.push(LuaValue::Bool(b));
1163}
1164
1165pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1166 state.push(LuaValue::LightUserData(p));
1167}
1168
1169pub fn push_thread(state: &mut LuaState) -> bool {
1171 let (value, is_main) = {
1172 let g = state.global();
1173 let id = g.current_thread_id;
1174 let v = g
1175 .thread_value_for(id)
1176 .expect("current_thread_id must always resolve to a registered thread");
1177 (v, id == g.main_thread_id)
1178 };
1179 state.push(LuaValue::Thread(value));
1180 is_main
1181}
1182
1183fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1186 let str_val = {
1187 let ts = state.intern_str(k)?;
1188 LuaValue::Str(ts)
1189 };
1190 let result = state.table_get_with_tm(&t, &str_val)?;
1193 state.push(result);
1194 let top = state.top_idx();
1195 Ok(state.get_at(top - 1).base_type())
1196}
1197
1198fn get_global_table(state: &LuaState) -> LuaValue {
1199 state.global().globals.clone()
1205}
1206
1207pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1208 let g = get_global_table(state);
1209 aux_get_str(state, g, name)
1210}
1211
1212pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1213 let t = index_to_value(state, idx);
1214 let top = state.top_idx();
1215 let key = state.get_at(top - 1);
1216 let result = state.table_get_with_tm(&t, &key)?;
1217 state.set_at(top - 1, result);
1218 let val = state.get_at(top - 1);
1219 Ok(val.base_type())
1220}
1221
1222pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1223 let t = index_to_value(state, idx);
1224 aux_get_str(state, t, k)
1225}
1226
1227pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1228 let t = index_to_value(state, idx);
1229 let key = LuaValue::Int(n);
1230 let result = state.table_get_with_tm(&t, &key)?;
1231 state.push(result);
1232 let top = state.top_idx();
1233 Ok(state.get_at(top - 1).base_type())
1234}
1235
1236pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1242 let key = LuaValue::Int(n);
1243 let result = state.table_get_with_tm(t, &key)?;
1244 state.push(result);
1245 let top = state.top_idx();
1246 Ok(state.get_at(top - 1).base_type())
1247}
1248
1249fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1250 let v = val.unwrap_or(LuaValue::Nil);
1251 state.push(v);
1252 let top = state.top_idx();
1253 state.get_at(top - 1).base_type()
1254}
1255
1256fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1257 let t = index_to_value(state, idx);
1258 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1259 if let LuaValue::Table(tbl) = t {
1260 Some(tbl)
1261 } else {
1262 None
1263 }
1264}
1265
1266pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1267 let t = get_table_value(state, idx);
1268 let top = state.top_idx();
1269 let key = state.get_at(top - 1);
1270 let val = t.as_ref().map(|tbl| tbl.get(&key));
1271 state.set_top_idx(top - 1);
1272 finish_raw_get(state, val)
1273}
1274
1275pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1276 let t = get_table_value(state, idx);
1277 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1278 finish_raw_get(state, val)
1279}
1280
1281pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1282 let t = get_table_value(state, idx);
1283 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1284 let val = t.as_ref().map(|tbl| tbl.get(&key));
1285 finish_raw_get(state, val)
1286}
1287
1288pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1289 let t = state.new_table();
1290 if narray > 0 || nrec > 0 {
1291 t.resize(state, narray as usize, nrec as usize)?;
1292 }
1293 state.push(LuaValue::Table(t));
1294 state.gc().check_step();
1295 Ok(())
1296}
1297
1298pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1299 let obj = index_to_value(state, objindex);
1300 let mt: Option<GcRef<LuaTable>> = match &obj {
1301 LuaValue::Table(t) => t.metatable(),
1302 LuaValue::UserData(u) => u.metatable(),
1303 other => {
1304 let idx = other.base_type() as usize;
1305 state.global().mt[idx].clone()
1306 }
1307 };
1308 if let Some(mt_table) = mt {
1309 state.push(LuaValue::Table(mt_table));
1310 true
1311 } else {
1312 false
1313 }
1314}
1315
1316pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1317 let o = index_to_value(state, idx);
1318 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1319 if let LuaValue::UserData(ref u) = o {
1320 let uv_count = u.uv.len() as i32;
1321 if n <= 0 || n > uv_count {
1322 state.push(LuaValue::Nil);
1323 LuaType::None
1324 } else {
1325 let val = u.uv[(n - 1) as usize].clone();
1326 let t = val.base_type();
1327 state.push(val);
1328 t
1329 }
1330 } else {
1331 state.push(LuaValue::Nil);
1332 LuaType::None
1333 }
1334}
1335
1336fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1339 let str_val = {
1340 let ts = state.intern_str(k)?;
1341 LuaValue::Str(ts)
1342 };
1343 let top = state.top_idx();
1348 let val = state.get_at(top - 1);
1349 state.table_set_with_tm(&t, str_val, val)?;
1350 state.pop();
1351 Ok(())
1352}
1353
1354pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1355 let g = get_global_table(state);
1356 aux_set_str(state, g, name)
1357}
1358
1359pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1360 let t = index_to_value(state, idx);
1361 let top = state.top_idx();
1362 let key = state.get_at(top - 2);
1363 let val = state.get_at(top - 1);
1364 state.table_set_with_tm(&t, key, val)?;
1365 state.set_top_idx(top - 2);
1366 Ok(())
1367}
1368
1369pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1370 let t = index_to_value(state, idx);
1371 aux_set_str(state, t, k)
1372}
1373
1374pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1375 let t = index_to_value(state, idx);
1376 let top = state.top_idx();
1377 let val = state.get_at(top - 1);
1378 let key = LuaValue::Int(n);
1379 state.table_set_with_tm(&t, key, val)?;
1380 state.pop();
1381 Ok(())
1382}
1383
1384pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1390 let top = state.top_idx();
1391 let val = state.get_at(top - 1);
1392 let key = LuaValue::Int(n);
1393 state.table_set_with_tm(t, key, val)?;
1394 state.pop();
1395 Ok(())
1396}
1397
1398fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1399 let t = get_table_value(state, idx)
1400 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1401 let top = state.top_idx();
1402 let val = state.get_at(top - 1);
1403 t.raw_set(state, key, val)?;
1404 t.invalidate_tm_cache();
1405 let top_val = state.get_at(top - 1);
1406 state.gc().barrier_back(&t, &top_val);
1407 state.set_top_idx(top - n as i32);
1408 Ok(())
1409}
1410
1411pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1412 let top = state.top_idx();
1413 let key = state.get_at(top - 2);
1414 aux_raw_set(state, idx, key, 2)
1415}
1416
1417pub fn raw_set_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> Result<(), LuaError> {
1418 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1419 aux_raw_set(state, idx, key, 1)
1420}
1421
1422pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1423 let t = get_table_value(state, idx)
1424 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1425 let top = state.top_idx();
1426 let val = state.get_at(top - 1);
1427 t.raw_set_int(state, n, val)?;
1428 let top_val = state.get_at(top - 1);
1429 state.gc().barrier_back(&t, &top_val);
1430 state.pop();
1431 Ok(())
1432}
1433
1434fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1439 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1440 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1441}
1442
1443fn register_finalizable_table(state: &mut LuaState, tbl: &GcRef<LuaTable>) {
1445 let already = state
1446 .global()
1447 .pending_finalizers
1448 .iter()
1449 .any(|t| GcRef::ptr_eq(t, tbl));
1450 if !already {
1451 state.global_mut().pending_finalizers.push(tbl.clone());
1452 }
1453}
1454
1455pub 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 = {
1503 let to_fin = &state.global().to_be_finalized;
1504 if to_fin.is_empty() { None } else { Some(to_fin.len() - 1) }
1505 };
1506 let Some(i) = target_idx else { break; };
1507 let tbl = state.global_mut().to_be_finalized.swap_remove(i);
1517 let mt = tbl.metatable();
1518 let gc_fn = match mt {
1519 Some(ref m) => {
1520 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1521 m.get_short_str(&name)
1522 }
1523 None => LuaValue::Nil,
1524 };
1525 if !matches!(gc_fn, LuaValue::Function(_)) {
1526 continue;
1527 }
1528 did_run = true;
1529 let saved_top = state.top_idx();
1530 let ci_top = state.current_call_info().top;
1531 if saved_top.0 < ci_top.0 {
1532 state.clear_stack_range(saved_top, ci_top);
1533 state.set_top(ci_top);
1534 }
1535 state.push(gc_fn);
1536 state.push(LuaValue::Table(tbl));
1537 let func_idx = state.top_idx() - 2;
1538 let _heap_guard = {
1539 let g = state.global.borrow();
1540 lua_gc::HeapGuard::push(&g.heap)
1541 };
1542 let old_allowhook = state.allowhook;
1543 let old_gcstp = state.global_mut().stop_gc_internal();
1544 state.allowhook = false;
1545 let caller_ci = state.ci;
1546 let caller_status = state.get_ci(caller_ci).callstatus;
1547 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1548 let status = crate::do_::pcall(
1549 state,
1550 |s| s.call_no_yield(func_idx, 0),
1551 func_idx,
1552 0,
1553 );
1554 let finalizer_error: Option<LuaValue> =
1558 if status != LuaStatus::Ok {
1559 Some(state.get_at(state.top_idx() - 1).clone())
1560 } else {
1561 None
1562 };
1563 state.get_ci_mut(caller_ci).callstatus = caller_status;
1564 state.allowhook = old_allowhook;
1565 state.global_mut().set_gc_stop_flags(old_gcstp);
1566 state.set_top(saved_top);
1567
1568 if let Some(errobj) = finalizer_error {
1569 match version {
1570 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1571 let msg: Vec<u8> = match &errobj {
1578 LuaValue::Str(s) => s.as_bytes().to_vec(),
1579 _ => b"no message".to_vec(),
1580 };
1581 let mut wrapped = b"error in __gc metamethod (".to_vec();
1582 wrapped.extend_from_slice(&msg);
1583 wrapped.push(b')');
1584 let interned = state.intern_str(&wrapped)?;
1585 return Err(LuaError::from_value(LuaValue::Str(interned)));
1586 }
1587 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1588 let msg: Vec<u8> = match &errobj {
1592 LuaValue::Str(s) => s.as_bytes().to_vec(),
1593 _ => b"error object is not a string".to_vec(),
1594 };
1595 state.emit_warning(b"error in ", true);
1596 state.emit_warning(b"__gc", true);
1597 state.emit_warning(b" (", true);
1598 state.emit_warning(&msg, true);
1599 state.emit_warning(b")", false);
1600 }
1601 _ => {}
1602 }
1603 }
1604 }
1605 let _ = did_run;
1609 Ok(())
1610}
1611
1612pub fn run_close_finalizers(state: &mut LuaState) {
1631 let pending: Vec<GcRef<lua_types::value::LuaTable>> =
1632 std::mem::take(&mut state.global_mut().pending_finalizers);
1633 if pending.is_empty() {
1634 return;
1635 }
1636 let mut seen = std::collections::HashSet::<usize>::new();
1637 {
1638 let mut g = state.global_mut();
1639 for tbl in pending {
1640 if seen.insert(tbl.identity()) {
1641 g.to_be_finalized.push(tbl);
1642 }
1643 }
1644 }
1645 run_pending_finalizers(state);
1646}
1647
1648fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1654 let mut g = state.global_mut();
1655 g.weak_tables_registry.retain(|w| w.strong_count() > 0);
1656 let mut seen = std::collections::HashSet::<usize>::new();
1657 g.weak_tables_registry
1658 .iter()
1659 .filter_map(|w| w.upgrade())
1660 .filter_map(|rc| {
1661 let id = rc.identity();
1662 if seen.insert(id) {
1663 Some(rc)
1664 } else {
1665 None
1666 }
1667 })
1668 .collect()
1669}
1670
1671pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1672 let top = state.top_idx();
1673 let mt_val = state.get_at(top - 1);
1674 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1675 None
1676 } else {
1677 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1678 if let LuaValue::Table(t) = mt_val {
1679 Some(t)
1680 } else {
1681 None
1682 }
1683 };
1684
1685 let obj = index_to_value(state, objindex);
1686 match obj {
1687 LuaValue::Table(ref tbl) => {
1688 if mt.is_some() {
1689 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1690 }
1691 tbl.set_metatable(mt.clone());
1692 if tbl.weak_mode() != 0 {
1693 state
1694 .global_mut()
1695 .weak_tables_registry
1696 .push(tbl.downgrade());
1697 }
1698 let tables_finalizable =
1708 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1709 if tables_finalizable {
1710 if let Some(ref mt_table) = mt {
1711 if metatable_has_gc(state, mt_table) {
1712 register_finalizable_table(state, tbl);
1713 }
1714 }
1715 }
1716 }
1717 LuaValue::UserData(ref ud) => {
1718 if let Some(ref mt_table) = mt {
1719 state.gc().obj_barrier(ud, mt_table);
1720 }
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 nuvalue = ud.uv.len() as i32;
1740 if n < 1 || n > nuvalue {
1741 false
1742 } else {
1743 state.gc().barrier_back(ud, &val);
1746 let _ = (n, ud);
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 }
1890 }
1891 }
1892 Ok(status)
1893}
1894
1895pub fn dump(
1896 state: &LuaState,
1897 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1898 strip: bool,
1899) -> Result<bool, LuaError> {
1900 let top = state.top_idx();
1901 let o = state.get_at(top - 1);
1902 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1903 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1904 Ok(true)
1905 } else {
1906 Ok(false)
1907 }
1908}
1909
1910pub fn status(state: &LuaState) -> LuaStatus {
1911 LuaStatus::from_raw(state.status as i32)
1912}
1913
1914#[repr(i32)]
1918#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1919pub enum GcWhat {
1920 Stop = 0,
1921 Restart = 1,
1922 Collect = 2,
1923 Count = 3,
1924 CountB = 4,
1925 Step = 5,
1926 SetPause = 6,
1927 SetStepMul = 7,
1928 IsRunning = 9,
1929 Gen = 10,
1930 Inc = 11,
1931}
1932
1933pub enum GcArgs {
1935 Stop,
1936 Restart,
1937 Collect,
1938 Count,
1939 CountB,
1940 Step { data: i32 },
1941 SetPause { value: i32 },
1942 SetStepMul { value: i32 },
1943 IsRunning,
1944 Gen { minormul: i32, majormul: i32 },
1945 Inc { pause: i32, stepmul: i32, stepsize: i32 },
1946 Param { param: usize, value: i64 },
1949}
1950
1951pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
1952 if let GcArgs::Param { param, value } = &args {
1956 return state.global_mut().gc55_param(*param, *value) as i32;
1957 }
1958 if state.global().is_gc_stopped_internally() {
1959 return -1;
1960 }
1961 match args {
1962 GcArgs::Stop => {
1963 state.global_mut().set_gc_stop_user();
1964 }
1965 GcArgs::Restart => {
1966 {
1967 let mut g = state.global_mut();
1968 crate::state::set_debt(&mut *g, 0);
1969 }
1970 state.global_mut().clear_gc_stop();
1971 }
1972 GcArgs::Collect => {
1973 if !state.allowhook {
1974 return 0;
1975 }
1976 state.gc().full_collect();
1982 if let Err(e) = run_pending_finalizers_inner(state, true) {
1990 state.global_mut().gc_finalizer_error = Some(e.into_value());
1991 }
1992 {
2001 let mut g = state.global_mut();
2002 crate::state::reclaim_dead_long_strings(&mut *g);
2003 }
2004 {
2012 let mut g = state.global_mut();
2013 let target_tb = 32_768_isize;
2014 let cur_tb = g.totalbytes + g.gc_debt;
2015 if cur_tb < target_tb {
2016 g.totalbytes += target_tb - cur_tb;
2017 }
2018 }
2019 }
2020 GcArgs::Count => {
2021 {
2022 let mut g = state.global_mut();
2023 crate::state::reclaim_dead_long_strings(&mut *g);
2024 }
2025 let g = state.global();
2026 let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
2027 let total = g.heap.bytes_used() + long_string_bytes;
2028 return (total >> 10) as i32;
2029 }
2030 GcArgs::CountB => {
2031 {
2032 let mut g = state.global_mut();
2033 crate::state::reclaim_dead_long_strings(&mut *g);
2034 }
2035 let g = state.global();
2036 let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
2037 let total = g.heap.bytes_used() + long_string_bytes;
2038 return (total & 0x3ff) as i32;
2039 }
2040 GcArgs::Step { data } => {
2041 let old_stp = {
2042 let mut g = state.global_mut();
2043 let old = g.gc_stop_flags();
2044 g.clear_gc_stop();
2045 old
2046 };
2047 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2054 let work_units = if data == 0 {
2055 stepmul
2056 } else {
2057 let raw = (data as isize).saturating_mul(stepmul);
2058 raw.max(1)
2059 };
2060 if data == 0 {
2061 let mut g = state.global_mut();
2062 crate::state::set_debt(&mut *g, 0);
2063 } else {
2064 let debt = data as isize * 1024 + state.global().gc_debt();
2065 let mut g = state.global_mut();
2066 crate::state::set_debt(&mut *g, debt);
2067 }
2068 let cycle_complete = state.gc().incremental_step(work_units);
2069 if state.global().is_gen_mode() {
2070 state.gc().prune_weak_tables_mark_only();
2071 }
2072 state.global_mut().set_gc_stop_flags(old_stp);
2073 if cycle_complete {
2080 let mut g = state.global_mut();
2081 let floor: isize = 1024;
2082 let cur_tb = g.totalbytes + g.gc_debt;
2083 let new_tb = (cur_tb / 2).max(floor);
2084 if new_tb < cur_tb {
2085 g.totalbytes -= cur_tb - new_tb;
2086 }
2087 }
2088 {
2090 let heap_state = state.global().heap.gc_state();
2091 let mut g = state.global_mut();
2092 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2093 }
2094 return if cycle_complete { 1 } else { 0 };
2095 }
2096 GcArgs::SetPause { value } => {
2097 let old = state.global().gc_pause_param() as i32;
2098 state.global_mut().set_gc_pause_param(value as u8);
2099 return old;
2100 }
2101 GcArgs::SetStepMul { value } => {
2102 let old = state.global().gc_stepmul_param() as i32;
2103 state.global_mut().set_gc_stepmul_param(value as u8);
2104 return old;
2105 }
2106 GcArgs::IsRunning => {
2107 return state.global().gc_running() as i32;
2108 }
2109 GcArgs::Gen { minormul, majormul } => {
2110 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2111 if minormul != 0 {
2112 state.global_mut().genminormul = minormul as u8;
2113 }
2114 if majormul != 0 {
2115 state.global_mut().set_gc_genmajormul(majormul as u8);
2116 }
2117 state.gc().change_mode(crate::state::GcKind::Generational);
2118 return old_mode;
2119 }
2120 GcArgs::Inc { pause, stepmul, stepsize } => {
2121 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2122 if pause != 0 {
2123 state.global_mut().set_gc_pause_param(pause as u8);
2124 }
2125 if stepmul != 0 {
2126 state.global_mut().set_gc_stepmul_param(stepmul as u8);
2127 }
2128 if stepsize != 0 {
2129 state.global_mut().gcstepsize = stepsize as u8;
2130 }
2131 state.gc().change_mode(crate::state::GcKind::Incremental);
2132 return old_mode;
2133 }
2134 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2135 }
2136 0
2137}
2138
2139pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2146 let top = state.top_idx();
2150 let errobj = state.get_at(top - 1);
2151 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2152 let memerr = state.global().memerrmsg.clone();
2153 GcRef::ptr_eq(s, &memerr)
2154 } else {
2155 false
2156 };
2157 if is_mem_err {
2158 Err(LuaError::Memory)
2159 } else {
2160 Err(LuaError::from_value(errobj))
2161 }
2162}
2163
2164pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2165 let t = get_table_value(state, idx)
2166 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2167 let top = state.top_idx();
2168 let key = state.get_at(top - 1);
2169 match t.next(key)? {
2170 Some((next_key, next_val)) => {
2171 state.set_at(top - 1, next_key);
2172 state.push(next_val);
2173 Ok(true)
2174 }
2175 None => {
2176 state.set_top_idx(top - 1);
2177 Ok(false)
2178 }
2179 }
2180}
2181
2182pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2183 let _level = index_to_stack_idx(state, idx);
2184 Ok(())
2187}
2188
2189pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2190 if n > 0 {
2191 state.concat(n)?;
2192 } else {
2193 let empty = state.intern_str(b"")?;
2194 state.push(LuaValue::Str(empty));
2195 }
2196 state.gc().check_step();
2197 Ok(())
2198}
2199
2200pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2201 let t = index_to_value(state, idx);
2202 let result = state.obj_len(&t)?;
2203 state.push(result);
2204 Ok(())
2205}
2206
2207pub fn set_warn_f(
2212 state: &mut LuaState,
2213 f: Option<Box<dyn FnMut(&[u8], bool)>>,
2214) {
2215 state.global_mut().warnf = f;
2217}
2218
2219pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2220 state.emit_warning(msg, tocont);
2221}
2222
2223pub fn new_userdata_uv(
2224 state: &mut LuaState,
2225 size: usize,
2226 nuvalue: i32,
2227) -> Result<GcRef<LuaUserData>, LuaError> {
2228 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2229 let u = state.new_userdata(size, nuvalue as usize)?;
2230 state.push(LuaValue::UserData(u.clone()));
2231 state.gc().check_step();
2232 Ok(u)
2233}
2234
2235fn aux_upvalue(
2241 state: &LuaState,
2242 fi: &LuaValue,
2243 n: i32,
2244) -> Option<(Vec<u8>, LuaValue)> {
2245 match fi {
2246 LuaValue::Function(LuaClosure::C(ccl)) => {
2247 let nupvalues = ccl.upvalues.len() as i32;
2248 if n < 1 || n > nupvalues {
2249 return None;
2250 }
2251 Some((Vec::new(), ccl.upvalues[(n - 1) as usize].clone()))
2252 }
2253 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2254 let nupvalues = lcl.upvals.len() as i32;
2255 if n < 1 || n > nupvalues {
2256 return None;
2257 }
2258 let val = state.upvalue_get(lcl, (n - 1) as usize);
2259 let name: Vec<u8> = lcl
2263 .proto
2264 .upvalues
2265 .get((n - 1) as usize)
2266 .and_then(|ud| ud.name.as_ref())
2267 .map(|s| s.as_bytes().to_vec())
2268 .unwrap_or_else(|| b"(no name)".to_vec());
2269 Some((name, val))
2270 }
2271 _ => None,
2272 }
2273}
2274
2275pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2276 let fi = index_to_value(state, funcindex);
2277 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2278 state.push(val);
2279 Some(name)
2280 } else {
2281 None
2282 }
2283}
2284
2285pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2286 let fi = index_to_value(state, funcindex);
2287 let (name, _) = aux_upvalue(state, &fi, n)?;
2288 let new_val = state.pop();
2289 match &fi {
2290 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2291 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2292 }
2293 LuaValue::Function(LuaClosure::C(_ccl)) => {
2294 let _ = new_val;
2297 }
2298 _ => return None,
2299 }
2300 Some(name)
2301}
2302
2303fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2306 let fi = index_to_value(state, fidx);
2307 debug_assert!(matches!(fi, LuaValue::Function(LuaClosure::Lua(_))), "Lua function expected");
2308 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2309 let sizeupvalues = lcl.upvals.len() as i32;
2310 if n >= 1 && n <= sizeupvalues {
2311 Some((n - 1) as usize)
2312 } else {
2313 None
2314 }
2315 } else {
2316 None
2317 }
2318}
2319
2320pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2322 let fi = index_to_value(state, fidx);
2323 match &fi {
2324 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2325 let idx = get_upval_ref_idx(state, fidx, n)?;
2326 Some(GcRef::identity(&lcl.upval(idx)))
2328 }
2329 LuaValue::Function(LuaClosure::C(ccl)) => {
2330 if n >= 1 && n <= ccl.upvalues.len() as i32 {
2331 Some(GcRef::identity(ccl) ^ (n as usize))
2334 } else {
2335 None
2336 }
2337 }
2338 LuaValue::Function(LuaClosure::LightC(_)) => None,
2339 _ => {
2340 debug_assert!(false, "function expected");
2341 None
2342 }
2343 }
2344}
2345
2346pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2348 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2349 Some(i) => i,
2350 None => return,
2351 };
2352 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2353 Some(i) => i,
2354 None => return,
2355 };
2356 let f1 = index_to_value(state, fidx1);
2357 let f2 = index_to_value(state, fidx2);
2358 if let (
2359 LuaValue::Function(LuaClosure::Lua(lcl1)),
2360 LuaValue::Function(LuaClosure::Lua(lcl2)),
2361 ) = (&f1, &f2)
2362 {
2363 let shared = lcl2.upval(idx2);
2364 lcl1.set_upval(idx1, shared);
2365 }
2366}
2367
2368