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(LuaError::arg_error(
541 arg,
542 "number has no integer representation",
543 ))
544 } else {
545 let got = index_to_value(self, arg);
546 let got_name = if lua_type_at(self, arg) == LuaType::None {
547 b"no value".to_vec()
548 } else {
549 crate::tagmethods::obj_type_name(self, &got)?
550 };
551 let extramsg = format!(
552 "number expected, got {}",
553 String::from_utf8_lossy(&got_name)
554 );
555 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
556 }
557 }
558 }
559 }
560
561 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
571 match to_number_x(self, arg) {
572 Some(d) => Ok(d),
573 None => {
574 let got = index_to_value(self, arg);
575 let got_name = if lua_type_at(self, arg) == LuaType::None {
576 b"no value".to_vec()
577 } else {
578 crate::tagmethods::obj_type_name(self, &got)?
579 };
580 let extramsg = format!(
581 "number expected, got {}",
582 String::from_utf8_lossy(&got_name)
583 );
584 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
585 }
586 }
587 }
588
589 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
599 match lua_type_at(self, arg) {
600 LuaType::None | LuaType::Nil => Ok(def),
601 _ => match to_integer_x(self, arg) {
602 Some(d) => Ok(d),
603 None => {
604 if is_number(self, arg) {
605 Err(LuaError::arg_error(
606 arg,
607 "number has no integer representation",
608 ))
609 } else {
610 let got = index_to_value(self, arg);
611 Err(LuaError::type_arg_error(arg, "number", &got))
612 }
613 }
614 },
615 }
616 }
617
618 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
625 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
626 }
627
628 pub fn protected_call_k(
632 &mut self,
633 nargs: i32,
634 nresults: i32,
635 msgh: i32,
636 ctx: isize,
637 k: Option<crate::state::LuaKFunction>,
638 ) -> Result<(), LuaError> {
639 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
640 }
641
642 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
643 push_lstring(self, s)?;
644 Ok(())
645 }
646
647 pub fn push_c_closure(
648 &mut self,
649 f: fn(&mut LuaState) -> Result<usize, LuaError>,
650 n: i32,
651 ) -> Result<(), LuaError> {
652 push_cclosure(self, f, n)
653 }
654
655 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
656 raw_set_i(self, idx, n)
657 }
658
659 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
660 set_i(self, idx, n)
661 }
662
663 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
667 get_i_value(self, t, n)
668 }
669
670 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
674 set_i_value(self, t, n)
675 }
676
677 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
678 create_table(self, narr, nrec)
679 }
680
681 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
685 set_field(self, LUA_REGISTRYINDEX, key)
686 }
687
688 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
694 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
695 return Ok(false);
696 }
697 self.pop_n(1);
698 create_table(self, 0, 2)?;
699 push_lstring(self, tname)?;
700 set_field(self, -2, b"__name")?;
701 push_value(self, -1);
702 set_field(self, LUA_REGISTRYINDEX, tname)?;
703 Ok(true)
704 }
705
706 pub fn new_lib(
713 &mut self,
714 funcs: &[(&[u8], LuaCFunction)],
715 ) -> Result<(), LuaError> {
716 create_table(self, 0, funcs.len() as i32)?;
717 for (name, f) in funcs {
718 push_cclosure(self, *f, 0)?;
719 set_field(self, -2, name)?;
720 }
721 Ok(())
722 }
723
724 pub fn register_lib(
730 &mut self,
731 _name: &[u8],
732 funcs: &[(&[u8], LuaCFunction)],
733 ) -> Result<(), LuaError> {
734 self.new_lib(funcs)
735 }
736
737 pub fn new_lib_table(
746 &mut self,
747 funcs: &[(&[u8], LuaCFunction)],
748 ) -> Result<(), LuaError> {
749 create_table(self, 0, funcs.len() as i32)
750 }
751
752 pub fn set_funcs_with_upvalues(
757 &mut self,
758 funcs: &[(&[u8], LuaCFunction)],
759 nup: i32,
760 ) -> Result<(), LuaError> {
761 check_stack(self, nup);
762 for (name, f) in funcs {
763 for _ in 0..nup {
764 push_value(self, -nup);
765 }
766 push_cclosure(self, *f, nup)?;
767 set_field(self, -(nup + 2), name)?;
768 }
769 self.pop_n(nup as usize);
770 Ok(())
771 }
772
773 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
774 set_metatable(self, objindex)?;
775 Ok(())
776 }
777
778 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
784 get_field(self, LUA_REGISTRYINDEX, name)?;
785 set_metatable(self, -2)?;
786 Ok(())
787 }
788
789 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
793 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
794 return Ok(true);
795 }
796 self.pop_n(1);
797 let idx = abs_index(self, LUA_REGISTRYINDEX);
798 let new_tbl = self.new_table();
799 self.push(LuaValue::Table(new_tbl));
800 push_value(self, -1);
801 set_field(self, idx, name)?;
802 Ok(false)
803 }
804
805 pub fn new_userdata_typed(
815 &mut self,
816 _name: &[u8],
817 size: usize,
818 nuvalue: i32,
819 ) -> Result<GcRef<LuaUserData>, LuaError> {
820 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
821 let u = GcRef::new(LuaUserData {
823 data: vec![0u8; size].into_boxed_slice(),
824 uv: vec![LuaValue::Nil; nuvalue as usize],
825 metatable: std::cell::RefCell::new(None),
826 host_value: std::cell::RefCell::new(None),
827 });
828 self.push(LuaValue::UserData(u.clone()));
829 self.gc().check_step();
830 Ok(u)
831 }
832}
833
834pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
837 if !is_valid_index(state, idx) {
838 return LuaType::None;
839 }
840 index_to_value(state, idx).base_type()
841}
842
843pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
844 t.type_name()
845}
846
847pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
848 let o = index_to_value(state, idx);
849 matches!(o, LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_)))
850}
851
852pub fn is_integer(state: &LuaState, idx: i32) -> bool {
853 let o = index_to_value(state, idx);
854 matches!(o, LuaValue::Int(_))
855}
856
857pub fn is_number(state: &LuaState, idx: i32) -> bool {
858 let o = index_to_value(state, idx);
859 o.to_number_with_strconv().is_some()
860}
861
862pub fn is_string(state: &LuaState, idx: i32) -> bool {
863 let o = index_to_value(state, idx);
864 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
865}
866
867pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
868 let o = index_to_value(state, idx);
869 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
870}
871
872pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
873 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
874 return false;
875 }
876 let o1 = index_to_value(state, index1);
877 let o2 = index_to_value(state, index2);
878 state.equal_obj(None, &o1, &o2)
879}
880
881pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
883 const LUA_OPUNM: i32 = 12;
886 const LUA_OPBNOT: i32 = 14;
887 if op == LUA_OPUNM || op == LUA_OPBNOT {
888 let top_val = state.get_at(state.top_idx() - 1);
890 state.push(top_val);
891 }
892 let top = state.top_idx();
893 let a = state.get_at(top - 2);
894 let b = state.get_at(top - 1);
895 let result = state.arith_op(op, &a, &b)?;
896 state.set_at(top - 2, result);
897 state.pop();
898 Ok(())
899}
900
901pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
902 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
903 let o1 = index_to_value(state, index1);
904 let o2 = index_to_value(state, index2);
905 if valid {
906 match op {
907 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
908 1 => state.less_than(&o1, &o2),
909 2 => state.less_equal(&o1, &o2),
910 _ => {
911 debug_assert!(false, "invalid option");
912 Ok(false)
913 }
914 }
915 } else {
916 Ok(false)
917 }
918}
919
920pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
921 match state.str_to_num(s) {
923 Some((val, consumed)) => {
924 state.push(val);
925 consumed
926 }
927 None => 0,
928 }
929}
930
931pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
932 let o = index_to_value(state, idx);
933 o.to_number_with_strconv()
934}
935
936pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
937 let o = index_to_value(state, idx);
938 o.to_integer_with_strconv()
939}
940
941pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
942 let o = index_to_value(state, idx);
943 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
944}
945
946pub fn to_lua_string(
948 state: &mut LuaState,
949 idx: i32,
950) -> Result<Option<GcRef<LuaString>>, LuaError> {
951 let o = index_to_value(state, idx);
952 if let LuaValue::Str(s) = &o {
953 return Ok(Some(s.clone()));
954 }
955 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
956 return Ok(None);
957 }
958 state.obj_to_string(idx)?;
959 state.gc().check_step();
960 let updated = index_to_value(state, idx);
961 if let LuaValue::Str(s) = updated {
962 Ok(Some(s))
963 } else {
964 Ok(None)
965 }
966}
967
968pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
969 let o = index_to_value(state, idx);
970 match &o {
971 LuaValue::Str(s) => s.len() as u64,
972 LuaValue::UserData(u) => u.len() as u64,
973 LuaValue::Table(t) => state.table_getn(t) as u64,
974 _ => 0,
975 }
976}
977
978pub fn to_cfunction(
979 state: &LuaState,
980 idx: i32,
981) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
982 let o = index_to_value(state, idx);
983 match o {
984 LuaValue::Function(LuaClosure::LightC(_f)) => None,
988 LuaValue::Function(LuaClosure::C(_ccl)) => None,
989 _ => None,
990 }
991}
992
993#[inline]
994fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
995 match o {
996 LuaValue::UserData(u) => {
997 let _ = u;
1001 None
1002 }
1003 LuaValue::LightUserData(p) => Some(*p),
1004 _ => None,
1005 }
1006}
1007
1008pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1009 let o = index_to_value(state, idx);
1010 to_userdata_ptr(&o)
1011}
1012
1013pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1014 let o = index_to_value(state, idx);
1018 if let LuaValue::Thread(t) = o {
1019 Some(t)
1020 } else {
1021 None
1022 }
1023}
1024
1025pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1028 let o = index_to_value(state, idx);
1029 match &o {
1032 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1033 LuaValue::LightUserData(p) => Some(*p as usize),
1034 LuaValue::Str(s) => Some(GcRef::identity(s)),
1035 LuaValue::Table(t) => Some(GcRef::identity(t)),
1036 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1037 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1038 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1039 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1040 _ => None,
1041 }
1042}
1043
1044pub fn push_nil(state: &mut LuaState) {
1047 state.push(LuaValue::Nil);
1048}
1049
1050pub fn push_number(state: &mut LuaState, n: f64) {
1051 state.push(LuaValue::Float(n));
1052}
1053
1054pub fn push_integer(state: &mut LuaState, n: i64) {
1055 state.push(LuaValue::Int(n));
1056}
1057
1058pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1060 let ts = state.intern_str(s)?;
1061 state.push(LuaValue::Str(ts.clone()));
1062 state.gc().check_step();
1063 Ok(ts)
1064}
1065
1066pub fn push_string(state: &mut LuaState, s: Option<&[u8]>) -> Result<Option<GcRef<LuaString>>, LuaError> {
1067 match s {
1068 None => {
1069 state.push(LuaValue::Nil);
1070 state.gc().check_step();
1071 Ok(None)
1072 }
1073 Some(bytes) => {
1074 let ts = state.intern_str(bytes)?;
1075 state.push(LuaValue::Str(ts.clone()));
1076 state.gc().check_step();
1077 Ok(Some(ts))
1078 }
1079 }
1080}
1081
1082pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1086 let ts = state.intern_str(formatted)?;
1087 state.push(LuaValue::Str(ts.clone()));
1088 state.gc().check_step();
1089 Ok(ts)
1090}
1091
1092pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1094 push_vfstring(state, formatted)
1095}
1096
1097pub fn push_cclosure(
1098 state: &mut LuaState,
1099 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1100 n: i32,
1101) -> Result<(), LuaError> {
1102 let idx: lua_types::closure::LuaCFnPtr = {
1116 let mut g = state.global_mut();
1117 if n == 0 {
1118 match g.c_functions.iter().position(|existing| {
1119 existing
1120 .as_bare()
1121 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1122 }) {
1123 Some(i) => i,
1124 None => {
1125 let i = g.c_functions.len();
1126 g.c_functions.push(LuaCallable::bare(f));
1127 i
1128 }
1129 }
1130 } else {
1131 let i = g.c_functions.len();
1132 g.c_functions.push(LuaCallable::bare(f));
1133 i
1134 }
1135 };
1136 if n == 0 {
1137 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1138 } else {
1139 debug_assert!(n > 0 && (n as u32) <= MAX_UPVAL as u32, "upvalue index too large");
1140 let n_usize = n as usize;
1141 let top = state.top_idx();
1142 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1143 let base = top.0 as usize - n_usize;
1144 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1145 for i in 0..n_usize {
1146 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1147 }
1148 state.pop_n(n_usize);
1149 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1151 func: idx,
1152 upvalues,
1153 }));
1154 state.push(LuaValue::Function(cl));
1155 state.gc().check_step();
1156 }
1157 Ok(())
1158}
1159
1160pub fn push_boolean(state: &mut LuaState, b: bool) {
1161 state.push(LuaValue::Bool(b));
1162}
1163
1164pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1165 state.push(LuaValue::LightUserData(p));
1166}
1167
1168pub fn push_thread(state: &mut LuaState) -> bool {
1170 let (value, is_main) = {
1171 let g = state.global();
1172 let id = g.current_thread_id;
1173 let v = g
1174 .thread_value_for(id)
1175 .expect("current_thread_id must always resolve to a registered thread");
1176 (v, id == g.main_thread_id)
1177 };
1178 state.push(LuaValue::Thread(value));
1179 is_main
1180}
1181
1182fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1185 let str_val = {
1186 let ts = state.intern_str(k)?;
1187 LuaValue::Str(ts)
1188 };
1189 let result = state.table_get_with_tm(&t, &str_val)?;
1192 state.push(result);
1193 let top = state.top_idx();
1194 Ok(state.get_at(top - 1).base_type())
1195}
1196
1197fn get_global_table(state: &LuaState) -> LuaValue {
1198 state.global().globals.clone()
1204}
1205
1206pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1207 let g = get_global_table(state);
1208 aux_get_str(state, g, name)
1209}
1210
1211pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1212 let t = index_to_value(state, idx);
1213 let top = state.top_idx();
1214 let key = state.get_at(top - 1);
1215 let result = state.table_get_with_tm(&t, &key)?;
1216 state.set_at(top - 1, result);
1217 let val = state.get_at(top - 1);
1218 Ok(val.base_type())
1219}
1220
1221pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1222 let t = index_to_value(state, idx);
1223 aux_get_str(state, t, k)
1224}
1225
1226pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1227 let t = index_to_value(state, idx);
1228 let key = LuaValue::Int(n);
1229 let result = state.table_get_with_tm(&t, &key)?;
1230 state.push(result);
1231 let top = state.top_idx();
1232 Ok(state.get_at(top - 1).base_type())
1233}
1234
1235pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1241 let key = LuaValue::Int(n);
1242 let result = state.table_get_with_tm(t, &key)?;
1243 state.push(result);
1244 let top = state.top_idx();
1245 Ok(state.get_at(top - 1).base_type())
1246}
1247
1248fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1249 let v = val.unwrap_or(LuaValue::Nil);
1250 state.push(v);
1251 let top = state.top_idx();
1252 state.get_at(top - 1).base_type()
1253}
1254
1255fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1256 let t = index_to_value(state, idx);
1257 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1258 if let LuaValue::Table(tbl) = t {
1259 Some(tbl)
1260 } else {
1261 None
1262 }
1263}
1264
1265pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1266 let t = get_table_value(state, idx);
1267 let top = state.top_idx();
1268 let key = state.get_at(top - 1);
1269 let val = t.as_ref().map(|tbl| tbl.get(&key));
1270 state.set_top_idx(top - 1);
1271 finish_raw_get(state, val)
1272}
1273
1274pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1275 let t = get_table_value(state, idx);
1276 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1277 finish_raw_get(state, val)
1278}
1279
1280pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1281 let t = get_table_value(state, idx);
1282 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1283 let val = t.as_ref().map(|tbl| tbl.get(&key));
1284 finish_raw_get(state, val)
1285}
1286
1287pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1288 let t = state.new_table();
1289 if narray > 0 || nrec > 0 {
1290 t.resize(state, narray as usize, nrec as usize)?;
1291 }
1292 state.push(LuaValue::Table(t));
1293 state.gc().check_step();
1294 Ok(())
1295}
1296
1297pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1298 let obj = index_to_value(state, objindex);
1299 let mt: Option<GcRef<LuaTable>> = match &obj {
1300 LuaValue::Table(t) => t.metatable(),
1301 LuaValue::UserData(u) => u.metatable(),
1302 other => {
1303 let idx = other.base_type() as usize;
1304 state.global().mt[idx].clone()
1305 }
1306 };
1307 if let Some(mt_table) = mt {
1308 state.push(LuaValue::Table(mt_table));
1309 true
1310 } else {
1311 false
1312 }
1313}
1314
1315pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1316 let o = index_to_value(state, idx);
1317 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1318 if let LuaValue::UserData(ref u) = o {
1319 let uv_count = u.uv.len() as i32;
1320 if n <= 0 || n > uv_count {
1321 state.push(LuaValue::Nil);
1322 LuaType::None
1323 } else {
1324 let val = u.uv[(n - 1) as usize].clone();
1325 let t = val.base_type();
1326 state.push(val);
1327 t
1328 }
1329 } else {
1330 state.push(LuaValue::Nil);
1331 LuaType::None
1332 }
1333}
1334
1335fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1338 let str_val = {
1339 let ts = state.intern_str(k)?;
1340 LuaValue::Str(ts)
1341 };
1342 let top = state.top_idx();
1347 let val = state.get_at(top - 1);
1348 state.table_set_with_tm(&t, str_val, val)?;
1349 state.pop();
1350 Ok(())
1351}
1352
1353pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1354 let g = get_global_table(state);
1355 aux_set_str(state, g, name)
1356}
1357
1358pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1359 let t = index_to_value(state, idx);
1360 let top = state.top_idx();
1361 let key = state.get_at(top - 2);
1362 let val = state.get_at(top - 1);
1363 state.table_set_with_tm(&t, key, val)?;
1364 state.set_top_idx(top - 2);
1365 Ok(())
1366}
1367
1368pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1369 let t = index_to_value(state, idx);
1370 aux_set_str(state, t, k)
1371}
1372
1373pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1374 let t = index_to_value(state, idx);
1375 let top = state.top_idx();
1376 let val = state.get_at(top - 1);
1377 let key = LuaValue::Int(n);
1378 state.table_set_with_tm(&t, key, val)?;
1379 state.pop();
1380 Ok(())
1381}
1382
1383pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1389 let top = state.top_idx();
1390 let val = state.get_at(top - 1);
1391 let key = LuaValue::Int(n);
1392 state.table_set_with_tm(t, key, val)?;
1393 state.pop();
1394 Ok(())
1395}
1396
1397fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1398 let t = get_table_value(state, idx)
1399 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1400 let top = state.top_idx();
1401 let val = state.get_at(top - 1);
1402 t.raw_set(state, key, val)?;
1403 t.invalidate_tm_cache();
1404 let top_val = state.get_at(top - 1);
1405 state.gc().barrier_back(&t, &top_val);
1406 state.set_top_idx(top - n as i32);
1407 Ok(())
1408}
1409
1410pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1411 let top = state.top_idx();
1412 let key = state.get_at(top - 2);
1413 aux_raw_set(state, idx, key, 2)
1414}
1415
1416pub fn raw_set_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> Result<(), LuaError> {
1417 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1418 aux_raw_set(state, idx, key, 1)
1419}
1420
1421pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1422 let t = get_table_value(state, idx)
1423 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1424 let top = state.top_idx();
1425 let val = state.get_at(top - 1);
1426 t.raw_set_int(state, n, val)?;
1427 let top_val = state.get_at(top - 1);
1428 state.gc().barrier_back(&t, &top_val);
1429 state.pop();
1430 Ok(())
1431}
1432
1433fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1438 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1439 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1440}
1441
1442fn register_finalizable_table(state: &mut LuaState, tbl: &GcRef<LuaTable>) {
1444 let already = state
1445 .global()
1446 .pending_finalizers
1447 .iter()
1448 .any(|t| GcRef::ptr_eq(t, tbl));
1449 if !already {
1450 state.global_mut().pending_finalizers.push(tbl.clone());
1451 }
1452}
1453
1454pub fn run_pending_finalizers(state: &mut LuaState) {
1468 let mut did_run = false;
1469 loop {
1470 let target_idx = {
1475 let to_fin = &state.global().to_be_finalized;
1476 if to_fin.is_empty() { None } else { Some(to_fin.len() - 1) }
1477 };
1478 let Some(i) = target_idx else { break; };
1479 let tbl = state.global_mut().to_be_finalized.swap_remove(i);
1489 let mt = tbl.metatable();
1490 let gc_fn = match mt {
1491 Some(ref m) => {
1492 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1493 m.get_short_str(&name)
1494 }
1495 None => LuaValue::Nil,
1496 };
1497 if !matches!(gc_fn, LuaValue::Function(_)) {
1498 continue;
1499 }
1500 did_run = true;
1501 let saved_top = state.top_idx();
1502 let ci_top = state.current_call_info().top;
1503 if saved_top.0 < ci_top.0 {
1504 state.clear_stack_range(saved_top, ci_top);
1505 state.set_top(ci_top);
1506 }
1507 state.push(gc_fn);
1508 state.push(LuaValue::Table(tbl));
1509 let func_idx = state.top_idx() - 2;
1510 let _heap_guard = {
1511 let g = state.global.borrow();
1512 lua_gc::HeapGuard::push(&g.heap)
1513 };
1514 let old_allowhook = state.allowhook;
1515 let old_gcstp = state.global_mut().stop_gc_internal();
1516 state.allowhook = false;
1517 let caller_ci = state.ci;
1518 let caller_status = state.get_ci(caller_ci).callstatus;
1519 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1520 let _ = crate::do_::pcall(
1521 state,
1522 |s| s.call_no_yield(func_idx, 0),
1523 func_idx,
1524 0,
1525 );
1526 state.get_ci_mut(caller_ci).callstatus = caller_status;
1527 state.allowhook = old_allowhook;
1528 state.global_mut().set_gc_stop_flags(old_gcstp);
1529 state.set_top(saved_top);
1530 }
1531 let _ = did_run;
1535}
1536
1537pub fn run_close_finalizers(state: &mut LuaState) {
1556 let pending: Vec<GcRef<lua_types::value::LuaTable>> =
1557 std::mem::take(&mut state.global_mut().pending_finalizers);
1558 if pending.is_empty() {
1559 return;
1560 }
1561 let mut seen = std::collections::HashSet::<usize>::new();
1562 {
1563 let mut g = state.global_mut();
1564 for tbl in pending {
1565 if seen.insert(tbl.identity()) {
1566 g.to_be_finalized.push(tbl);
1567 }
1568 }
1569 }
1570 run_pending_finalizers(state);
1571}
1572
1573fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1579 let mut g = state.global_mut();
1580 g.weak_tables_registry.retain(|w| w.strong_count() > 0);
1581 let mut seen = std::collections::HashSet::<usize>::new();
1582 g.weak_tables_registry
1583 .iter()
1584 .filter_map(|w| w.upgrade())
1585 .filter_map(|rc| {
1586 let id = rc.identity();
1587 if seen.insert(id) {
1588 Some(rc)
1589 } else {
1590 None
1591 }
1592 })
1593 .collect()
1594}
1595
1596pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1597 let top = state.top_idx();
1598 let mt_val = state.get_at(top - 1);
1599 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1600 None
1601 } else {
1602 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1603 if let LuaValue::Table(t) = mt_val {
1604 Some(t)
1605 } else {
1606 None
1607 }
1608 };
1609
1610 let obj = index_to_value(state, objindex);
1611 match obj {
1612 LuaValue::Table(ref tbl) => {
1613 if mt.is_some() {
1614 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1615 }
1616 tbl.set_metatable(mt.clone());
1617 if tbl.weak_mode() != 0 {
1618 state
1619 .global_mut()
1620 .weak_tables_registry
1621 .push(tbl.downgrade());
1622 }
1623 if let Some(ref mt_table) = mt {
1628 if metatable_has_gc(state, mt_table) {
1629 register_finalizable_table(state, tbl);
1630 }
1631 }
1632 }
1633 LuaValue::UserData(ref ud) => {
1634 if let Some(ref mt_table) = mt {
1635 state.gc().obj_barrier(ud, mt_table);
1636 }
1638 ud.set_metatable(mt);
1639 }
1640 ref other => {
1641 let idx = other.base_type() as usize;
1642 state.global_mut().mt[idx] = mt;
1643 }
1644 }
1645 state.pop();
1646 Ok(true)
1647}
1648
1649pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1650 let o = index_to_value(state, idx);
1651 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1652 let top = state.top_idx();
1653 let val = state.get_at(top - 1);
1654 let res = if let LuaValue::UserData(ref ud) = o {
1655 let nuvalue = ud.uv.len() as i32;
1656 if n < 1 || n > nuvalue {
1657 false
1658 } else {
1659 state.gc().barrier_back(ud, &val);
1662 let _ = (n, ud);
1663 true
1664 }
1665 } else {
1666 false
1667 };
1668 state.pop();
1669 Ok(res)
1670}
1671
1672pub fn call_k(
1676 state: &mut LuaState,
1677 nargs: i32,
1678 nresults: i32,
1679 ctx: isize,
1680 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1681) -> Result<(), LuaError> {
1682 let top = state.top_idx();
1683 let func_idx = top - (nargs + 1);
1684 if k.is_some() && state.is_yieldable() {
1690 let ci_idx = state.ci;
1691 {
1692 let ci = state.get_ci_mut(ci_idx);
1693 ci.set_u_c_k(k);
1694 ci.set_u_c_ctx(ctx);
1695 }
1696 state.call_at(func_idx, nresults)?;
1697 } else {
1698 state.call_no_yield(func_idx, nresults)?;
1699 }
1700 state.adjust_results(nresults);
1701 Ok(())
1702}
1703
1704pub fn pcall_k(
1706 state: &mut LuaState,
1707 nargs: i32,
1708 nresults: i32,
1709 errfunc: i32,
1710 ctx: isize,
1711 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
1712) -> Result<LuaStatus, LuaError> {
1713 let _heap_guard = {
1718 let g = state.global.borrow();
1719 lua_gc::HeapGuard::push(&g.heap)
1724 };
1725 let err_handler_idx: isize = if errfunc == 0 {
1726 0
1727 } else {
1728 let o = index_to_stack_idx(state, errfunc);
1729 debug_assert!(
1730 matches!(state.get_at(o), LuaValue::Function(_)),
1731 "error handler must be a function"
1732 );
1733 o.0 as isize
1734 };
1735 let top = state.top_idx();
1736 let func_idx = top - (nargs + 1);
1737 if k.is_none() || !state.is_yieldable() {
1738 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
1739 state.adjust_results(nresults);
1740 return Ok(LuaStatus::Ok);
1741 }
1742 let ci_idx = state.ci;
1748 let allow = state.allowhook;
1749 let saved_errfunc = state.errfunc;
1750 {
1751 let ci = state.get_ci_mut(ci_idx);
1752 ci.set_u_c_k(k);
1753 ci.set_u_c_ctx(ctx);
1754 ci.set_u2_funcidx(func_idx.0 as i32);
1755 ci.set_u_c_old_errfunc(saved_errfunc);
1756 ci.set_oah(allow);
1757 ci.callstatus |= crate::state::CIST_YPCALL;
1758 }
1759 state.errfunc = err_handler_idx;
1760 let call_result = crate::do_::call(state, func_idx, nresults);
1761 match call_result {
1762 Ok(()) => {
1763 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
1766 state.errfunc = saved_errfunc;
1767 state.adjust_results(nresults);
1768 Ok(LuaStatus::Ok)
1769 }
1770 Err(crate::state::LuaError::Yield) => {
1771 Err(crate::state::LuaError::Yield)
1775 }
1776 Err(e) => {
1777 Err(e)
1781 }
1782 }
1783}
1784
1785pub fn load(
1789 state: &mut LuaState,
1790 reader: Box<dyn FnMut() -> Option<Vec<u8>>>,
1791 chunkname: Option<&[u8]>,
1792 mode: Option<&[u8]>,
1793) -> Result<LuaStatus, LuaError> {
1794 let name = chunkname.unwrap_or(b"?");
1795 let z = crate::zio::ZIO::new(reader);
1796 let status = state.protected_parser(z, name, mode);
1797 if status == LuaStatus::Ok {
1798 let top = state.top_idx();
1799 let func_val = state.get_at(top - 1);
1800 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
1801 if !lcl.upvals.is_empty() {
1802 let gt = get_global_table(state);
1803 let uv = state.new_upval_closed(gt);
1804 lcl.set_upval(0, uv);
1805 }
1806 }
1807 }
1808 Ok(status)
1809}
1810
1811pub fn dump(
1812 state: &LuaState,
1813 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
1814 strip: bool,
1815) -> Result<bool, LuaError> {
1816 let top = state.top_idx();
1817 let o = state.get_at(top - 1);
1818 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
1819 crate::dump::dump(state, &lcl.proto, writer, strip)?;
1820 Ok(true)
1821 } else {
1822 Ok(false)
1823 }
1824}
1825
1826pub fn status(state: &LuaState) -> LuaStatus {
1827 LuaStatus::from_raw(state.status as i32)
1828}
1829
1830#[repr(i32)]
1834#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1835pub enum GcWhat {
1836 Stop = 0,
1837 Restart = 1,
1838 Collect = 2,
1839 Count = 3,
1840 CountB = 4,
1841 Step = 5,
1842 SetPause = 6,
1843 SetStepMul = 7,
1844 IsRunning = 9,
1845 Gen = 10,
1846 Inc = 11,
1847}
1848
1849pub enum GcArgs {
1851 Stop,
1852 Restart,
1853 Collect,
1854 Count,
1855 CountB,
1856 Step { data: i32 },
1857 SetPause { value: i32 },
1858 SetStepMul { value: i32 },
1859 IsRunning,
1860 Gen { minormul: i32, majormul: i32 },
1861 Inc { pause: i32, stepmul: i32, stepsize: i32 },
1862}
1863
1864pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
1865 if state.global().is_gc_stopped_internally() {
1866 return -1;
1867 }
1868 match args {
1869 GcArgs::Stop => {
1870 state.global_mut().set_gc_stop_user();
1871 }
1872 GcArgs::Restart => {
1873 {
1874 let mut g = state.global_mut();
1875 crate::state::set_debt(&mut *g, 0);
1876 }
1877 state.global_mut().clear_gc_stop();
1878 }
1879 GcArgs::Collect => {
1880 if !state.allowhook {
1881 return 0;
1882 }
1883 state.gc().full_collect();
1889 run_pending_finalizers(state);
1893 {
1902 let mut g = state.global_mut();
1903 crate::state::reclaim_dead_long_strings(&mut *g);
1904 }
1905 {
1913 let mut g = state.global_mut();
1914 let target_tb = 32_768_isize;
1915 let cur_tb = g.totalbytes + g.gc_debt;
1916 if cur_tb < target_tb {
1917 g.totalbytes += target_tb - cur_tb;
1918 }
1919 }
1920 }
1921 GcArgs::Count => {
1922 {
1923 let mut g = state.global_mut();
1924 crate::state::reclaim_dead_long_strings(&mut *g);
1925 }
1926 let g = state.global();
1927 let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
1928 let total = g.heap.bytes_used() + long_string_bytes;
1929 return (total >> 10) as i32;
1930 }
1931 GcArgs::CountB => {
1932 {
1933 let mut g = state.global_mut();
1934 crate::state::reclaim_dead_long_strings(&mut *g);
1935 }
1936 let g = state.global();
1937 let long_string_bytes: usize = g.gc_tracked_long_strings.iter().map(|(_, sz)| sz).sum();
1938 let total = g.heap.bytes_used() + long_string_bytes;
1939 return (total & 0x3ff) as i32;
1940 }
1941 GcArgs::Step { data } => {
1942 let old_stp = {
1943 let mut g = state.global_mut();
1944 let old = g.gc_stop_flags();
1945 g.clear_gc_stop();
1946 old
1947 };
1948 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
1955 let work_units = if data == 0 {
1956 stepmul
1957 } else {
1958 let raw = (data as isize).saturating_mul(stepmul);
1959 raw.max(1)
1960 };
1961 if data == 0 {
1962 let mut g = state.global_mut();
1963 crate::state::set_debt(&mut *g, 0);
1964 } else {
1965 let debt = data as isize * 1024 + state.global().gc_debt();
1966 let mut g = state.global_mut();
1967 crate::state::set_debt(&mut *g, debt);
1968 }
1969 let cycle_complete = state.gc().incremental_step(work_units);
1970 if state.global().is_gen_mode() {
1971 state.gc().prune_weak_tables_mark_only();
1972 }
1973 state.global_mut().set_gc_stop_flags(old_stp);
1974 if cycle_complete {
1981 let mut g = state.global_mut();
1982 let floor: isize = 1024;
1983 let cur_tb = g.totalbytes + g.gc_debt;
1984 let new_tb = (cur_tb / 2).max(floor);
1985 if new_tb < cur_tb {
1986 g.totalbytes -= cur_tb - new_tb;
1987 }
1988 }
1989 {
1991 let heap_state = state.global().heap.gc_state();
1992 let mut g = state.global_mut();
1993 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
1994 }
1995 return if cycle_complete { 1 } else { 0 };
1996 }
1997 GcArgs::SetPause { value } => {
1998 let old = state.global().gc_pause_param() as i32;
1999 state.global_mut().set_gc_pause_param(value as u8);
2000 return old;
2001 }
2002 GcArgs::SetStepMul { value } => {
2003 let old = state.global().gc_stepmul_param() as i32;
2004 state.global_mut().set_gc_stepmul_param(value as u8);
2005 return old;
2006 }
2007 GcArgs::IsRunning => {
2008 return state.global().gc_running() as i32;
2009 }
2010 GcArgs::Gen { minormul, majormul } => {
2011 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2012 if minormul != 0 {
2013 state.global_mut().genminormul = minormul as u8;
2014 }
2015 if majormul != 0 {
2016 state.global_mut().set_gc_genmajormul(majormul as u8);
2017 }
2018 state.gc().change_mode(crate::state::GcKind::Generational);
2019 return old_mode;
2020 }
2021 GcArgs::Inc { pause, stepmul, stepsize } => {
2022 let old_mode = if state.global().is_gen_mode() { 10i32 } else { 11i32 };
2023 if pause != 0 {
2024 state.global_mut().set_gc_pause_param(pause as u8);
2025 }
2026 if stepmul != 0 {
2027 state.global_mut().set_gc_stepmul_param(stepmul as u8);
2028 }
2029 if stepsize != 0 {
2030 state.global_mut().gcstepsize = stepsize as u8;
2031 }
2032 state.gc().change_mode(crate::state::GcKind::Incremental);
2033 return old_mode;
2034 }
2035 }
2036 0
2037}
2038
2039pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2046 let top = state.top_idx();
2050 let errobj = state.get_at(top - 1);
2051 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2052 let memerr = state.global().memerrmsg.clone();
2053 GcRef::ptr_eq(s, &memerr)
2054 } else {
2055 false
2056 };
2057 if is_mem_err {
2058 Err(LuaError::Memory)
2059 } else {
2060 Err(LuaError::from_value(errobj))
2061 }
2062}
2063
2064pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2065 let t = get_table_value(state, idx)
2066 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2067 let top = state.top_idx();
2068 let key = state.get_at(top - 1);
2069 match t.next(key)? {
2070 Some((next_key, next_val)) => {
2071 state.set_at(top - 1, next_key);
2072 state.push(next_val);
2073 Ok(true)
2074 }
2075 None => {
2076 state.set_top_idx(top - 1);
2077 Ok(false)
2078 }
2079 }
2080}
2081
2082pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2083 let _level = index_to_stack_idx(state, idx);
2084 Ok(())
2087}
2088
2089pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2090 if n > 0 {
2091 state.concat(n)?;
2092 } else {
2093 let empty = state.intern_str(b"")?;
2094 state.push(LuaValue::Str(empty));
2095 }
2096 state.gc().check_step();
2097 Ok(())
2098}
2099
2100pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2101 let t = index_to_value(state, idx);
2102 let result = state.obj_len(&t)?;
2103 state.push(result);
2104 Ok(())
2105}
2106
2107pub fn set_warn_f(
2112 state: &mut LuaState,
2113 f: Option<Box<dyn FnMut(&[u8], bool)>>,
2114) {
2115 state.global_mut().warnf = f;
2117}
2118
2119pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2120 state.emit_warning(msg, tocont);
2121}
2122
2123pub fn new_userdata_uv(
2124 state: &mut LuaState,
2125 size: usize,
2126 nuvalue: i32,
2127) -> Result<GcRef<LuaUserData>, LuaError> {
2128 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2129 let u = state.new_userdata(size, nuvalue as usize)?;
2130 state.push(LuaValue::UserData(u.clone()));
2131 state.gc().check_step();
2132 Ok(u)
2133}
2134
2135fn aux_upvalue(
2141 state: &LuaState,
2142 fi: &LuaValue,
2143 n: i32,
2144) -> Option<(Vec<u8>, LuaValue)> {
2145 match fi {
2146 LuaValue::Function(LuaClosure::C(ccl)) => {
2147 let nupvalues = ccl.upvalues.len() as i32;
2148 if n < 1 || n > nupvalues {
2149 return None;
2150 }
2151 Some((Vec::new(), ccl.upvalues[(n - 1) as usize].clone()))
2152 }
2153 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2154 let nupvalues = lcl.upvals.len() as i32;
2155 if n < 1 || n > nupvalues {
2156 return None;
2157 }
2158 let val = state.upvalue_get(lcl, (n - 1) as usize);
2159 let name: Vec<u8> = lcl
2163 .proto
2164 .upvalues
2165 .get((n - 1) as usize)
2166 .and_then(|ud| ud.name.as_ref())
2167 .map(|s| s.as_bytes().to_vec())
2168 .unwrap_or_else(|| b"(no name)".to_vec());
2169 Some((name, val))
2170 }
2171 _ => None,
2172 }
2173}
2174
2175pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2176 let fi = index_to_value(state, funcindex);
2177 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2178 state.push(val);
2179 Some(name)
2180 } else {
2181 None
2182 }
2183}
2184
2185pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2186 let fi = index_to_value(state, funcindex);
2187 let (name, _) = aux_upvalue(state, &fi, n)?;
2188 let new_val = state.pop();
2189 match &fi {
2190 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2191 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2192 }
2193 LuaValue::Function(LuaClosure::C(_ccl)) => {
2194 let _ = new_val;
2197 }
2198 _ => return None,
2199 }
2200 Some(name)
2201}
2202
2203fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2206 let fi = index_to_value(state, fidx);
2207 debug_assert!(matches!(fi, LuaValue::Function(LuaClosure::Lua(_))), "Lua function expected");
2208 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2209 let sizeupvalues = lcl.upvals.len() as i32;
2210 if n >= 1 && n <= sizeupvalues {
2211 Some((n - 1) as usize)
2212 } else {
2213 None
2214 }
2215 } else {
2216 None
2217 }
2218}
2219
2220pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2222 let fi = index_to_value(state, fidx);
2223 match &fi {
2224 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2225 let idx = get_upval_ref_idx(state, fidx, n)?;
2226 Some(GcRef::identity(&lcl.upval(idx)))
2228 }
2229 LuaValue::Function(LuaClosure::C(ccl)) => {
2230 if n >= 1 && n <= ccl.upvalues.len() as i32 {
2231 Some(GcRef::identity(ccl) ^ (n as usize))
2234 } else {
2235 None
2236 }
2237 }
2238 LuaValue::Function(LuaClosure::LightC(_)) => None,
2239 _ => {
2240 debug_assert!(false, "function expected");
2241 None
2242 }
2243 }
2244}
2245
2246pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2248 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2249 Some(i) => i,
2250 None => return,
2251 };
2252 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2253 Some(i) => i,
2254 None => return,
2255 };
2256 let f1 = index_to_value(state, fidx1);
2257 let f2 = index_to_value(state, fidx2);
2258 if let (
2259 LuaValue::Function(LuaClosure::Lua(lcl1)),
2260 LuaValue::Function(LuaClosure::Lua(lcl2)),
2261 ) = (&f1, &f2)
2262 {
2263 let shared = lcl2.upval(idx2);
2264 lcl1.set_upval(idx1, shared);
2265 }
2266}
2267
2268