1#![allow(dead_code)]
9
10#[allow(unused_imports)]
11use crate::prelude::*;
12use std::convert::Infallible;
13
14use crate::state::{
15 FinalizerObject, LuaCFunction, LuaCallable, LuaState, LuaTableRefExt, LuaTypeExt,
16 LuaUserDataRefExt, LuaValueExt, StackIdx, StackIdxExt, WeakTableEntry,
17};
18use lua_types::value::LuaTable;
19use lua_types::{
20 GcRef, LuaClosure, LuaError, LuaStatus, LuaString, LuaType, LuaUserData, LuaValue,
21};
22
23pub const LUA_IDENT: &[u8] = b"$LuaVersion: Lua 5.4.7 Copyright (C) 1994-2024 Lua.org, PUC-Rio $\
24 $LuaAuthors: R. Ierusalimschy, L. H. Figueiredo, W. Celes $";
25
26const LUA_REGISTRYINDEX: i32 = -(1_000_000) - 1000;
27
28const LUA_MULTRET: i32 = -1;
29
30const LUA_RIDX_GLOBALS: i64 = 2;
31
32const MAX_UPVAL: u8 = 255;
33
34#[inline]
35fn is_pseudo(idx: i32) -> bool {
36 idx <= LUA_REGISTRYINDEX
37}
38
39#[inline]
40fn is_upvalue(idx: i32) -> bool {
41 idx < LUA_REGISTRYINDEX
42}
43
44#[inline]
49fn is_valid_index(state: &LuaState, idx: i32) -> bool {
50 if idx == 0 {
51 return false;
52 }
53 let ci = state.current_call_info();
54 if idx > 0 {
55 let slot = ci.func + idx;
56 slot.0 < state.top_idx().0
57 } else if !is_pseudo(idx) {
58 (-idx) as u32 <= state.top_idx().0.saturating_sub(ci.func.0 + 1)
59 } else if idx == LUA_REGISTRYINDEX {
60 true
61 } else {
62 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
63 let func_val = state.get_at(ci.func);
64 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
65 let upvalues = ccl.upvalues.borrow();
66 upval_n >= 1 && upval_n <= upvalues.len()
67 } else {
68 false
69 }
70 }
71}
72
73fn index_to_value(state: &LuaState, idx: i32) -> LuaValue {
78 let ci = state.current_call_info();
79 if idx > 0 {
80 let func_idx = ci.func;
81 let slot = func_idx + idx;
82 debug_assert!(
83 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
84 "unacceptable index"
85 );
86 if slot.0 >= state.top_idx().0 {
87 LuaValue::Nil
88 } else {
89 state.get_at(slot)
90 }
91 } else if !is_pseudo(idx) {
92 debug_assert!(idx != 0, "invalid index");
94 let top = state.top_idx();
95 let slot = (top.0 as i32 + idx) as u32;
96 state.get_at(slot)
97 } else if idx == LUA_REGISTRYINDEX {
98 state.registry_value()
99 } else {
100 let upval_n = (LUA_REGISTRYINDEX - idx) as usize;
102 debug_assert!(upval_n <= MAX_UPVAL as usize + 1, "upvalue index too large");
103 let func_val = state.get_at(ci.func);
104 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
105 let upvalues = ccl.upvalues.borrow();
107 if upval_n >= 1 && upval_n <= upvalues.len() {
108 upvalues[upval_n - 1].clone()
109 } else {
110 LuaValue::Nil
111 }
112 } else {
113 LuaValue::Nil
114 }
115 }
116}
117
118#[inline(always)]
125pub fn positive_index_value(state: &LuaState, idx: i32) -> LuaValue {
126 debug_assert!(idx > 0, "positive_index_value requires a positive index");
127 let ci = state.current_call_info();
128 let func_idx = ci.func;
129 let slot = func_idx + idx;
130 debug_assert!(
131 idx as u32 <= ci.top.saturating_sub(func_idx + 1),
132 "unacceptable index"
133 );
134 if slot.0 >= state.top_idx().0 {
135 LuaValue::Nil
136 } else {
137 state.get_at(slot)
138 }
139}
140
141#[inline]
143fn index_to_stack_idx(state: &LuaState, idx: i32) -> StackIdx {
144 let ci = state.current_call_info();
145 if idx > 0 {
146 let slot = ci.func + idx;
147 debug_assert!(slot.0 < state.top_idx().0, "invalid index");
148 slot
149 } else {
150 debug_assert!(idx != 0 && !is_pseudo(idx), "invalid index");
151 StackIdx((state.top_idx().0 as i32 + idx) as u32)
152 }
153}
154
155pub fn check_stack(state: &mut LuaState, n: i32) -> bool {
158 debug_assert!(n >= 0, "negative 'n'");
159 let available = state.stack_available();
160 let res = if available > n as usize {
161 true
162 } else {
163 crate::do_::grow_stack(state, n, false).unwrap_or(false)
164 };
165 if res {
166 let needed_top = state.top_idx() + n as i32;
167 let ci_idx = state.current_ci_idx();
168 if state.get_ci(ci_idx).top.0 < needed_top.0 {
169 let live_top = state.top_idx();
170 state.get_ci_mut(ci_idx).top = needed_top;
171 state.clear_stack_range(live_top, needed_top);
172 }
173 }
174 res
175}
176
177pub fn xmove(from: &mut LuaState, to: &mut LuaState, n: i32) {
193 if n <= 0 {
194 return;
195 }
196 if std::ptr::eq(from as *const LuaState, to as *const LuaState) {
197 return;
198 }
199 let abs_top = from.top_idx().0 as i32;
200 debug_assert!(abs_top >= n, "lua_xmove: from stack underflow");
201 let first_abs = abs_top - n;
202 let mut buf: Vec<lua_types::LuaValue> = Vec::with_capacity(n as usize);
203 for i in 0..n {
204 let idx = StackIdx((first_abs + i) as u32);
205 buf.push(from.get_at(idx));
206 }
207 from.set_top(StackIdx(first_abs as u32));
208 for v in buf {
209 to.push(v);
210 }
211}
212
213pub fn at_panic(
214 state: &mut LuaState,
215 panicf: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
216) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
217 let old = state.global_mut().panic;
218 state.global_mut().panic = panicf;
219 old
220}
221
222pub fn version(_state: &LuaState) -> f64 {
223 504.0
224}
225
226pub fn abs_index(state: &LuaState, idx: i32) -> i32 {
227 if idx > 0 || is_pseudo(idx) {
229 idx
230 } else {
231 let ci = state.current_call_info();
232 (state.top_idx().0 as i32 - ci.func.0 as i32) + idx
233 }
234}
235
236pub fn get_top(state: &LuaState) -> i32 {
237 let ci = state.current_call_info();
238 (state.top_idx().0 as i32) - (ci.func.0 as i32 + 1)
239}
240
241pub fn set_top(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
242 let func = state.current_call_info().func;
243 let ci_top = state.current_call_info().top;
244 if idx >= 0 {
245 debug_assert!(
246 idx as u32 <= ci_top.saturating_sub(func + 1),
247 "new top too large"
248 );
249 let new_top = func + 1 + idx as i32;
250 let old_top = state.top_idx();
251 if new_top.0 > old_top.0 {
252 for i in old_top.0..new_top.0 {
253 state.set_at(i, LuaValue::Nil);
254 }
255 }
256 state.set_top_idx(new_top);
257 } else {
258 debug_assert!(
259 -(idx + 1) <= (state.top_idx().0 as i32 - (func.0 as i32 + 1)),
260 "invalid new top"
261 );
262 let new_top = (state.top_idx().0 as i32 + idx + 1) as u32;
263 state.set_top_idx(new_top);
264 }
265 Ok(())
266}
267
268pub fn close_slot(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
269 let level = index_to_stack_idx(state, idx);
270 state.set_at(level, LuaValue::Nil);
271 Ok(())
272}
273
274#[inline]
275fn reverse_segment(state: &mut LuaState, from: StackIdx, to: StackIdx) {
276 let mut lo = from.0;
277 let mut hi = to.0;
278 while lo < hi {
279 let temp = state.get_at(StackIdx(lo));
280 let hi_val = state.get_at(StackIdx(hi));
281 state.set_at(StackIdx(lo), hi_val);
282 state.set_at(StackIdx(hi), temp);
283 lo += 1;
284 hi -= 1;
285 }
286}
287
288pub fn rotate(state: &mut LuaState, idx: i32, n: i32) {
289 let t = state.top_idx() - 1;
290 let p = index_to_stack_idx(state, idx);
291 debug_assert!(
292 (n.unsigned_abs() as i32) <= ((t.0 as i32) - (p.0 as i32) + 1),
293 "invalid 'n'"
294 );
295 let m = if n >= 0 {
296 t - n
297 } else {
298 StackIdx((p.0 as i32 - n - 1) as u32)
299 };
300 reverse_segment(state, p, m);
301 reverse_segment(state, m + 1, t);
302 reverse_segment(state, p, t);
303}
304
305pub fn copy(state: &mut LuaState, fromidx: i32, toidx: i32) {
306 let fr = index_to_value(state, fromidx);
307 if is_upvalue(toidx) {
308 let upval_n = (LUA_REGISTRYINDEX - toidx) as usize;
310 let func_val = state.get_at(state.current_call_info().func);
311 if let LuaValue::Function(LuaClosure::C(ref ccl)) = func_val {
312 let wrote = {
313 let mut upvalues = ccl.upvalues.borrow_mut();
314 if upval_n >= 1 && upval_n <= upvalues.len() {
315 upvalues[upval_n - 1] = fr.clone();
316 true
317 } else {
318 false
319 }
320 };
321 if wrote {
322 state.gc().barrier(ccl, &fr);
323 }
324 }
325 } else if toidx == LUA_REGISTRYINDEX {
326 } else {
327 let to_slot = index_to_stack_idx(state, toidx);
328 state.set_at(to_slot, fr);
329 }
330}
331
332pub fn push_value(state: &mut LuaState, idx: i32) {
333 let v = index_to_value(state, idx);
334 state.push(v);
335}
336
337impl LuaState {
342 pub fn push_copy(&mut self, idx: i32) -> Result<(), LuaError> {
343 push_value(self, idx);
344 Ok(())
345 }
346
347 pub fn push_value_at(&mut self, idx: i32) -> Result<(), LuaError> {
348 push_value(self, idx);
349 Ok(())
350 }
351
352 pub fn insert(&mut self, idx: i32) -> Result<(), LuaError> {
353 rotate(self, idx, 1);
354 Ok(())
355 }
356
357 pub fn length_at(&mut self, idx: i32) -> Result<i64, LuaError> {
363 len(self, idx)?;
364 let l = match to_integer_x(self, -1) {
365 Some(n) => n,
366 None => {
367 return Err(LuaError::runtime(format_args!(
368 "object length is not an integer"
369 )));
370 }
371 };
372 self.pop_n(1);
373 Ok(l)
374 }
375
376 pub fn write_output(&mut self, msg: &[u8]) -> Result<(), LuaError> {
383 if let Some(write_fn) = self.global().stdout_hook {
384 write_fn(msg).map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
385 return Ok(());
386 }
387
388 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
389 {
390 let _ = msg;
391 Err(LuaError::runtime(format_args!(
392 "stdout not available in this host"
393 )))
394 }
395
396 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
397 {
398 use std::io::Write;
399 let stdout = std::io::stdout();
400 let mut handle = stdout.lock();
401 handle
402 .write_all(msg)
403 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
404 handle
405 .flush()
406 .map_err(|e| LuaError::runtime(format_args!("{}", e)))?;
407 Ok(())
408 }
409 }
410
411 pub fn to_display_string(&mut self, idx: i32) -> Result<Vec<u8>, LuaError> {
420 let abs = abs_index(self, idx);
421 let v = index_to_value(self, abs);
422 let mt: Option<GcRef<LuaTable>> = match &v {
423 LuaValue::Table(t) => t.metatable(),
424 LuaValue::UserData(u) => u.metatable(),
425 _ => self.global().mt[v.base_type() as usize].clone(),
426 };
427 if let Some(mt_ref) = mt {
428 let key = self.intern_str(b"__tostring")?;
429 let f = mt_ref.get_short_str(&key);
430 if !matches!(f, LuaValue::Nil) {
431 let func_idx = self.top_idx();
432 self.push(f);
433 self.push(v.clone());
434 if self.current_ci().is_lua_code() {
435 self.do_call(func_idx, 1)?;
436 } else {
437 self.do_call_no_yield(func_idx, 1)?;
438 }
439 let top = self.top_idx();
440 let result = self.get_at(StackIdx(top.0 - 1));
441 if let LuaValue::Str(s) = result {
442 return Ok(s.as_bytes().to_vec());
443 }
444 let legacy_no_check = matches!(
445 self.global().lua_version,
446 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
447 );
448 if matches!(result, LuaValue::Int(_) | LuaValue::Float(_)) {
449 return Ok(crate::object::num_to_string(self, &result)?
450 .as_bytes()
451 .to_vec());
452 }
453 if legacy_no_check {
454 let display_idx = StackIdx(top.0 - 1).0 as i32;
455 return display_default_bytes(self, &result, display_idx);
456 }
457 return Err(crate::debug::c_api_runtime(
458 self,
459 b"'__tostring' must return a string".to_vec(),
460 ));
461 }
462 }
463 let bytes: Vec<u8> = match &v {
464 LuaValue::Str(s) => {
465 let out = s.as_bytes().to_vec();
466 self.push(LuaValue::Str(s.clone()));
467 out
468 }
469 LuaValue::Int(_) | LuaValue::Float(_) => {
470 let s = crate::object::num_to_string(self, &v)?;
471 let out = s.as_bytes().to_vec();
472 self.push(LuaValue::Str(s));
473 out
474 }
475 LuaValue::Bool(b) => {
476 let lit: &[u8] = if *b { b"true" } else { b"false" };
477 let s = self.intern_str(lit)?;
478 self.push(LuaValue::Str(s));
479 lit.to_vec()
480 }
481 LuaValue::Nil => {
482 let s = self.intern_str(b"nil")?;
483 self.push(LuaValue::Str(s));
484 b"nil".to_vec()
485 }
486 _ => {
487 let kind = crate::tagmethods::obj_type_name(self, &v)?;
488 let ptr = to_pointer(self, abs).unwrap_or(0);
489 let mut buf = kind;
490 buf.extend_from_slice(b": 0x");
491 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
492 let s = self.intern_str(&buf)?;
493 self.push(LuaValue::Str(s));
494 buf
495 }
496 };
497 Ok(bytes)
498 }
499
500 pub fn top(&mut self) -> i32 {
507 get_top(self)
508 }
509
510 pub fn get_top(&mut self) -> i32 {
514 get_top(self)
515 }
516
517 pub fn type_at(&mut self, idx: i32) -> LuaType {
522 lua_type_at(self, idx)
523 }
524
525 pub fn check_arg_any(&mut self, arg: i32) -> Result<(), LuaError> {
531 if lua_type_at(self, arg) == LuaType::None {
532 return Err(crate::debug::arg_error_impl(self, arg, b"value expected"));
533 }
534 Ok(())
535 }
536
537 pub fn check_arg_string(&mut self, arg: i32) -> Result<Vec<u8>, LuaError> {
547 match to_lua_string(self, arg)? {
548 Some(s) => Ok(s.as_bytes().to_vec()),
549 None => {
550 let got = index_to_value(self, arg);
551 let got_name = if lua_type_at(self, arg) == LuaType::None {
552 b"no value".to_vec()
553 } else {
554 crate::tagmethods::obj_type_name(self, &got)?
555 };
556 let extramsg = format!(
557 "string expected, got {}",
558 String::from_utf8_lossy(&got_name)
559 );
560 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
561 }
562 }
563 }
564
565 pub fn check_arg_integer(&mut self, arg: i32) -> Result<i64, LuaError> {
577 match to_integer_x(self, arg) {
578 Some(d) => Ok(d),
579 None => {
580 if is_number(self, arg) {
581 Err(crate::debug::arg_error_impl(
582 self,
583 arg,
584 b"number has no integer representation",
585 ))
586 } else {
587 let got = index_to_value(self, arg);
588 let got_name = if lua_type_at(self, arg) == LuaType::None {
589 b"no value".to_vec()
590 } else {
591 crate::tagmethods::obj_type_name(self, &got)?
592 };
593 let extramsg = format!(
594 "number expected, got {}",
595 String::from_utf8_lossy(&got_name)
596 );
597 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
598 }
599 }
600 }
601 }
602
603 pub fn check_number(&mut self, arg: i32) -> Result<f64, LuaError> {
613 match to_number_x(self, arg) {
614 Some(d) => Ok(d),
615 None => {
616 let got = index_to_value(self, arg);
617 let got_name = if lua_type_at(self, arg) == LuaType::None {
618 b"no value".to_vec()
619 } else {
620 crate::tagmethods::obj_type_name(self, &got)?
621 };
622 let extramsg = format!(
623 "number expected, got {}",
624 String::from_utf8_lossy(&got_name)
625 );
626 Err(crate::debug::arg_error_impl(self, arg, extramsg.as_bytes()))
627 }
628 }
629 }
630
631 pub fn opt_arg_integer(&mut self, arg: i32, def: i64) -> Result<i64, LuaError> {
641 match lua_type_at(self, arg) {
642 LuaType::None | LuaType::Nil => Ok(def),
643 _ => match to_integer_x(self, arg) {
644 Some(d) => Ok(d),
645 None => {
646 if is_number(self, arg) {
647 Err(LuaError::arg_error(
648 arg,
649 "number has no integer representation",
650 ))
651 } else {
652 let got = index_to_value(self, arg);
653 Err(LuaError::type_arg_error(arg, "number", &got))
654 }
655 }
656 },
657 }
658 }
659
660 pub fn protected_call(&mut self, nargs: i32, nresults: i32, msgh: i32) -> Result<(), LuaError> {
667 pcall_k(self, nargs, nresults, msgh, 0, None).map(|_| ())
668 }
669
670 pub fn protected_call_k(
674 &mut self,
675 nargs: i32,
676 nresults: i32,
677 msgh: i32,
678 ctx: isize,
679 k: Option<crate::state::LuaKFunction>,
680 ) -> Result<(), LuaError> {
681 pcall_k(self, nargs, nresults, msgh, ctx, k).map(|_| ())
682 }
683
684 pub fn push_string(&mut self, s: &[u8]) -> Result<(), LuaError> {
685 push_lstring(self, s)?;
686 Ok(())
687 }
688
689 pub fn push_c_closure(
690 &mut self,
691 f: fn(&mut LuaState) -> Result<usize, LuaError>,
692 n: i32,
693 ) -> Result<(), LuaError> {
694 push_cclosure(self, f, n)
695 }
696
697 pub fn raw_seti(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
698 raw_set_i(self, idx, n)
699 }
700
701 pub fn table_set_i(&mut self, idx: i32, n: i64) -> Result<(), LuaError> {
702 set_i(self, idx, n)
703 }
704
705 pub fn table_get_i_value(&mut self, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
709 get_i_value(self, t, n)
710 }
711
712 pub fn table_set_i_value(&mut self, t: &LuaValue, n: i64) -> Result<(), LuaError> {
716 set_i_value(self, t, n)
717 }
718
719 pub fn create_table(&mut self, narr: i32, nrec: i32) -> Result<(), LuaError> {
720 create_table(self, narr, nrec)
721 }
722
723 pub fn registry_set(&mut self, key: &[u8]) -> Result<(), LuaError> {
727 set_field(self, LUA_REGISTRYINDEX, key)
728 }
729
730 pub fn new_metatable(&mut self, tname: &[u8]) -> Result<bool, LuaError> {
736 if get_field(self, LUA_REGISTRYINDEX, tname)? != LuaType::Nil {
737 return Ok(false);
738 }
739 self.pop_n(1);
740 create_table(self, 0, 2)?;
741 push_lstring(self, tname)?;
742 set_field(self, -2, b"__name")?;
743 push_value(self, -1);
744 set_field(self, LUA_REGISTRYINDEX, tname)?;
745 Ok(true)
746 }
747
748 pub fn new_lib(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
755 create_table(self, 0, funcs.len() as i32)?;
756 for (name, f) in funcs {
757 push_cclosure(self, *f, 0)?;
758 set_field(self, -2, name)?;
759 }
760 Ok(())
761 }
762
763 pub fn register_lib(
769 &mut self,
770 _name: &[u8],
771 funcs: &[(&[u8], LuaCFunction)],
772 ) -> Result<(), LuaError> {
773 self.new_lib(funcs)
774 }
775
776 pub fn new_lib_table(&mut self, funcs: &[(&[u8], LuaCFunction)]) -> Result<(), LuaError> {
785 create_table(self, 0, funcs.len() as i32)
786 }
787
788 pub fn set_funcs_with_upvalues(
793 &mut self,
794 funcs: &[(&[u8], LuaCFunction)],
795 nup: i32,
796 ) -> Result<(), LuaError> {
797 check_stack(self, nup);
798 for (name, f) in funcs {
799 for _ in 0..nup {
800 push_value(self, -nup);
801 }
802 push_cclosure(self, *f, nup)?;
803 set_field(self, -(nup + 2), name)?;
804 }
805 self.pop_n(nup as usize);
806 Ok(())
807 }
808
809 pub fn set_metatable(&mut self, objindex: i32) -> Result<(), LuaError> {
810 set_metatable(self, objindex)?;
811 Ok(())
812 }
813
814 pub fn set_metatable_by_name(&mut self, name: &[u8]) -> Result<(), LuaError> {
820 get_field(self, LUA_REGISTRYINDEX, name)?;
821 set_metatable(self, -2)?;
822 Ok(())
823 }
824
825 pub fn get_subtable_registry(&mut self, name: &[u8]) -> Result<bool, LuaError> {
829 if get_field(self, LUA_REGISTRYINDEX, name)? == LuaType::Table {
830 return Ok(true);
831 }
832 self.pop_n(1);
833 let idx = abs_index(self, LUA_REGISTRYINDEX);
834 let new_tbl = self.new_table();
835 self.push(LuaValue::Table(new_tbl));
836 push_value(self, -1);
837 set_field(self, idx, name)?;
838 Ok(false)
839 }
840
841 pub fn new_userdata_typed(
851 &mut self,
852 _name: &[u8],
853 size: usize,
854 nuvalue: i32,
855 ) -> Result<GcRef<LuaUserData>, LuaError> {
856 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
857 let u = GcRef::new(LuaUserData {
861 data: vec![0u8; size].into_boxed_slice(),
862 uv: std::cell::RefCell::new(vec![LuaValue::Nil; nuvalue as usize]),
863 metatable: std::cell::RefCell::new(None),
864 host_value: std::cell::RefCell::new(None),
865 });
866 u.account_buffer(u.buffer_bytes() as isize);
867 self.push(LuaValue::UserData(u.clone()));
868 self.gc_pre_collect_clear();
869 self.gc().check_step();
870 Ok(u)
871 }
872}
873
874fn value_pointer(o: &LuaValue) -> Option<usize> {
878 match o {
879 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
880 LuaValue::LightUserData(p) => Some(*p as usize),
881 LuaValue::Str(s) => Some(GcRef::identity(s)),
882 LuaValue::Table(t) => Some(GcRef::identity(t)),
883 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
884 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
885 LuaValue::UserData(u) => Some(GcRef::identity(u)),
886 LuaValue::Thread(t) => Some(GcRef::identity(t)),
887 _ => None,
888 }
889}
890
891fn display_default_bytes(
897 state: &mut LuaState,
898 val: &LuaValue,
899 _idx: i32,
900) -> Result<Vec<u8>, LuaError> {
901 match val {
902 LuaValue::Str(s) => Ok(s.as_bytes().to_vec()),
903 LuaValue::Int(_) | LuaValue::Float(_) => {
904 Ok(crate::object::num_to_string(state, val)?.as_bytes().to_vec())
905 }
906 LuaValue::Bool(b) => Ok(if *b { b"true".to_vec() } else { b"false".to_vec() }),
907 LuaValue::Nil => Ok(b"nil".to_vec()),
908 _ => {
909 let kind = crate::tagmethods::obj_type_name(state, val)?;
910 let ptr = value_pointer(val).unwrap_or(0);
911 let mut buf = kind;
912 buf.extend_from_slice(b": 0x");
913 buf.extend_from_slice(format!("{:x}", ptr).as_bytes());
914 Ok(buf)
915 }
916 }
917}
918
919pub fn lua_type_at(state: &LuaState, idx: i32) -> LuaType {
922 if !is_valid_index(state, idx) {
923 return LuaType::None;
924 }
925 index_to_value(state, idx).base_type()
926}
927
928pub fn type_name(_state: &LuaState, t: LuaType) -> &'static [u8] {
929 t.type_name()
930}
931
932pub fn is_cfunction(state: &LuaState, idx: i32) -> bool {
933 let o = index_to_value(state, idx);
934 matches!(
935 o,
936 LuaValue::Function(LuaClosure::LightC(_)) | LuaValue::Function(LuaClosure::C(_))
937 )
938}
939
940pub fn is_integer(state: &LuaState, idx: i32) -> bool {
941 let o = index_to_value(state, idx);
942 matches!(o, LuaValue::Int(_))
943}
944
945pub fn is_number(state: &LuaState, idx: i32) -> bool {
946 let o = index_to_value(state, idx);
947 o.to_number_with_strconv().is_some()
948}
949
950pub fn is_string(state: &LuaState, idx: i32) -> bool {
951 let o = index_to_value(state, idx);
952 matches!(o, LuaValue::Str(_) | LuaValue::Int(_) | LuaValue::Float(_))
953}
954
955pub fn is_userdata(state: &LuaState, idx: i32) -> bool {
956 let o = index_to_value(state, idx);
957 matches!(o, LuaValue::UserData(_) | LuaValue::LightUserData(_))
958}
959
960pub fn raw_equal(state: &LuaState, index1: i32, index2: i32) -> bool {
961 if !is_valid_index(state, index1) || !is_valid_index(state, index2) {
962 return false;
963 }
964 let o1 = index_to_value(state, index1);
965 let o2 = index_to_value(state, index2);
966 state.equal_obj(None, &o1, &o2)
967}
968
969pub fn arith(state: &mut LuaState, op: i32) -> Result<(), LuaError> {
971 const LUA_OPUNM: i32 = 12;
972 const LUA_OPBNOT: i32 = 14;
973 if op == LUA_OPUNM || op == LUA_OPBNOT {
974 let top_val = state.get_at(state.top_idx() - 1);
976 state.push(top_val);
977 }
978 let top = state.top_idx();
979 let a = state.get_at(top - 2);
980 let b = state.get_at(top - 1);
981 let result = state.arith_op(op, &a, &b)?;
982 state.set_at(top - 2, result);
983 state.pop();
984 Ok(())
985}
986
987pub fn compare(state: &mut LuaState, index1: i32, index2: i32, op: i32) -> Result<bool, LuaError> {
988 let valid = is_valid_index(state, index1) && is_valid_index(state, index2);
989 let o1 = index_to_value(state, index1);
990 let o2 = index_to_value(state, index2);
991 if valid {
992 match op {
993 0 => Ok(state.equal_obj_with_tm(&o1, &o2)?),
994 1 => state.less_than(&o1, &o2),
995 2 => state.less_equal(&o1, &o2),
996 _ => {
997 debug_assert!(false, "invalid option");
998 Ok(false)
999 }
1000 }
1001 } else {
1002 Ok(false)
1003 }
1004}
1005
1006pub fn string_to_number(state: &mut LuaState, s: &[u8]) -> usize {
1007 match state.str_to_num(s) {
1008 Some((val, consumed)) => {
1009 state.push(val);
1010 consumed
1011 }
1012 None => 0,
1013 }
1014}
1015
1016pub fn to_number_x(state: &LuaState, idx: i32) -> Option<f64> {
1017 let o = index_to_value(state, idx);
1018 o.to_number_with_strconv()
1019}
1020
1021pub fn to_integer_x(state: &LuaState, idx: i32) -> Option<i64> {
1022 let o = index_to_value(state, idx);
1023 o.to_integer_with_strconv()
1024}
1025
1026pub fn to_boolean(state: &LuaState, idx: i32) -> bool {
1027 let o = index_to_value(state, idx);
1028 !matches!(o, LuaValue::Nil | LuaValue::Bool(false))
1029}
1030
1031pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Option<GcRef<LuaString>>, LuaError> {
1033 let o = index_to_value(state, idx);
1034 if let LuaValue::Str(s) = &o {
1035 return Ok(Some(s.clone()));
1036 }
1037 if !matches!(o, LuaValue::Int(_) | LuaValue::Float(_)) {
1038 return Ok(None);
1039 }
1040 state.obj_to_string(idx)?;
1041 state.gc_pre_collect_clear();
1042 state.gc().check_step();
1043 let updated = index_to_value(state, idx);
1044 if let LuaValue::Str(s) = updated {
1045 Ok(Some(s))
1046 } else {
1047 Ok(None)
1048 }
1049}
1050
1051pub fn raw_len(state: &LuaState, idx: i32) -> u64 {
1052 let o = index_to_value(state, idx);
1053 match &o {
1054 LuaValue::Str(s) => s.len() as u64,
1055 LuaValue::UserData(u) => u.len() as u64,
1056 LuaValue::Table(t) => state.table_getn(t) as u64,
1057 _ => 0,
1058 }
1059}
1060
1061pub fn to_cfunction(
1062 state: &LuaState,
1063 idx: i32,
1064) -> Option<fn(&mut LuaState) -> Result<usize, LuaError>> {
1065 let o = index_to_value(state, idx);
1066 match o {
1067 LuaValue::Function(LuaClosure::LightC(_f)) => None,
1070 LuaValue::Function(LuaClosure::C(_ccl)) => None,
1071 _ => None,
1072 }
1073}
1074
1075#[inline]
1076fn to_userdata_ptr(o: &LuaValue) -> Option<*mut core::ffi::c_void> {
1077 match o {
1078 LuaValue::UserData(u) => {
1079 let _ = u;
1083 None
1084 }
1085 LuaValue::LightUserData(p) => Some(*p),
1086 _ => None,
1087 }
1088}
1089
1090pub fn to_userdata(state: &LuaState, idx: i32) -> Option<*mut core::ffi::c_void> {
1091 let o = index_to_value(state, idx);
1092 to_userdata_ptr(&o)
1093}
1094
1095pub fn to_thread(state: &LuaState, idx: i32) -> Option<GcRef<lua_types::value::LuaThread>> {
1096 let o = index_to_value(state, idx);
1100 if let LuaValue::Thread(t) = o {
1101 Some(t)
1102 } else {
1103 None
1104 }
1105}
1106
1107pub fn to_pointer(state: &LuaState, idx: i32) -> Option<usize> {
1110 let o = index_to_value(state, idx);
1111 match &o {
1112 LuaValue::Function(LuaClosure::LightC(f)) => Some(*f as usize),
1113 LuaValue::LightUserData(p) => Some(*p as usize),
1114 LuaValue::Str(s) => Some(GcRef::identity(s)),
1115 LuaValue::Table(t) => Some(GcRef::identity(t)),
1116 LuaValue::Function(LuaClosure::Lua(f)) => Some(GcRef::identity(f)),
1117 LuaValue::Function(LuaClosure::C(f)) => Some(GcRef::identity(f)),
1118 LuaValue::UserData(u) => Some(GcRef::identity(u)),
1119 LuaValue::Thread(t) => Some(GcRef::identity(t)),
1120 _ => None,
1121 }
1122}
1123
1124pub fn push_nil(state: &mut LuaState) {
1127 state.push(LuaValue::Nil);
1128}
1129
1130pub fn push_number(state: &mut LuaState, n: f64) {
1131 state.push(LuaValue::Float(n));
1132}
1133
1134pub fn push_integer(state: &mut LuaState, n: i64) {
1135 state.push(LuaValue::Int(n));
1136}
1137
1138pub fn push_lstring(state: &mut LuaState, s: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1140 let ts = state.intern_str(s)?;
1141 state.push(LuaValue::Str(ts.clone()));
1142 state.gc_pre_collect_clear();
1143 state.gc().check_step();
1144 Ok(ts)
1145}
1146
1147pub fn push_string(
1148 state: &mut LuaState,
1149 s: Option<&[u8]>,
1150) -> Result<Option<GcRef<LuaString>>, LuaError> {
1151 match s {
1152 None => {
1153 state.push(LuaValue::Nil);
1154 state.gc_pre_collect_clear();
1155 state.gc().check_step();
1156 Ok(None)
1157 }
1158 Some(bytes) => {
1159 let ts = state.intern_str(bytes)?;
1160 state.push(LuaValue::Str(ts.clone()));
1161 state.gc_pre_collect_clear();
1162 state.gc().check_step();
1163 Ok(Some(ts))
1164 }
1165 }
1166}
1167
1168pub fn push_vfstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1172 let ts = state.intern_str(formatted)?;
1173 state.push(LuaValue::Str(ts.clone()));
1174 state.gc_pre_collect_clear();
1175 state.gc().check_step();
1176 Ok(ts)
1177}
1178
1179pub fn push_fstring(state: &mut LuaState, formatted: &[u8]) -> Result<GcRef<LuaString>, LuaError> {
1181 push_vfstring(state, formatted)
1182}
1183
1184pub fn push_cclosure(
1185 state: &mut LuaState,
1186 f: fn(&mut LuaState) -> Result<usize, LuaError>,
1187 n: i32,
1188) -> Result<(), LuaError> {
1189 let idx: lua_types::closure::LuaCFnPtr = {
1194 let mut g = state.global_mut();
1195 if n == 0 {
1196 match g.c_functions.iter().position(|existing| {
1197 existing
1198 .as_bare()
1199 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, f))
1200 }) {
1201 Some(i) => i,
1202 None => {
1203 let i = g.c_functions.len();
1204 g.c_functions.push(LuaCallable::bare(f));
1205 i
1206 }
1207 }
1208 } else {
1209 let i = g.c_functions.len();
1210 g.c_functions.push(LuaCallable::bare(f));
1211 i
1212 }
1213 };
1214 if n == 0 {
1215 state.push(LuaValue::Function(LuaClosure::LightC(idx)));
1216 } else {
1217 debug_assert!(
1218 n > 0 && (n as u32) <= MAX_UPVAL as u32,
1219 "upvalue index too large"
1220 );
1221 let n_usize = n as usize;
1222 let top = state.top_idx();
1223 debug_assert!((top.0 as usize) >= n_usize, "not enough elements on stack");
1224 let base = top.0 as usize - n_usize;
1225 let mut upvalues: Vec<LuaValue> = Vec::with_capacity(n_usize);
1226 for i in 0..n_usize {
1227 upvalues.push(state.get_at(crate::state::StackIdx((base + i) as u32)));
1228 }
1229 state.pop_n(n_usize);
1230 let cl = LuaClosure::C(GcRef::new(lua_types::closure::LuaCClosure {
1234 func: idx,
1235 upvalues: std::cell::RefCell::new(upvalues),
1236 }));
1237 if let LuaClosure::C(ccl) = &cl {
1238 ccl.account_buffer(ccl.buffer_bytes() as isize);
1239 }
1240 state.push(LuaValue::Function(cl));
1241 state.gc_pre_collect_clear();
1242 state.gc().check_step();
1243 }
1244 Ok(())
1245}
1246
1247pub fn push_boolean(state: &mut LuaState, b: bool) {
1248 state.push(LuaValue::Bool(b));
1249}
1250
1251pub fn push_light_userdata(state: &mut LuaState, p: *mut core::ffi::c_void) {
1252 state.push(LuaValue::LightUserData(p));
1253}
1254
1255pub fn push_thread(state: &mut LuaState) -> bool {
1257 let (value, is_main) = {
1258 let g = state.global();
1259 let id = g.current_thread_id;
1260 let v = g
1261 .thread_value_for(id)
1262 .expect("current_thread_id must always resolve to a registered thread");
1263 (v, id == g.main_thread_id)
1264 };
1265 state.push(LuaValue::Thread(value));
1266 is_main
1267}
1268
1269fn aux_get_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<LuaType, LuaError> {
1272 let str_val = {
1273 let ts = state.intern_str(k)?;
1274 LuaValue::Str(ts)
1275 };
1276 let result = state.table_get_with_tm(&t, &str_val)?;
1277 state.push(result);
1278 let top = state.top_idx();
1279 Ok(state.get_at(top - 1).base_type())
1280}
1281
1282fn get_global_table(state: &LuaState) -> LuaValue {
1283 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1293 let running = state.global().current_thread_id;
1294 return state.v51_thread_lgt(running);
1295 }
1296 state.global().globals.clone()
1297}
1298
1299pub fn get_global(state: &mut LuaState, name: &[u8]) -> Result<LuaType, LuaError> {
1300 let g = get_global_table(state);
1301 aux_get_str(state, g, name)
1302}
1303
1304pub fn get_table(state: &mut LuaState, idx: i32) -> Result<LuaType, LuaError> {
1305 let t = index_to_value(state, idx);
1306 let top = state.top_idx();
1307 let key = state.get_at(top - 1);
1308 let result = state.table_get_with_tm(&t, &key)?;
1309 state.set_at(top - 1, result);
1310 let val = state.get_at(top - 1);
1311 Ok(val.base_type())
1312}
1313
1314pub fn get_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<LuaType, LuaError> {
1315 let t = index_to_value(state, idx);
1316 aux_get_str(state, t, k)
1317}
1318
1319pub fn get_i(state: &mut LuaState, idx: i32, n: i64) -> Result<LuaType, LuaError> {
1320 let t = index_to_value(state, idx);
1321 let key = LuaValue::Int(n);
1322 let result = state.table_get_with_tm(&t, &key)?;
1323 state.push(result);
1324 let top = state.top_idx();
1325 Ok(state.get_at(top - 1).base_type())
1326}
1327
1328pub fn get_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<LuaType, LuaError> {
1334 let key = LuaValue::Int(n);
1335 let result = state.table_get_with_tm(t, &key)?;
1336 state.push(result);
1337 let top = state.top_idx();
1338 Ok(state.get_at(top - 1).base_type())
1339}
1340
1341fn finish_raw_get(state: &mut LuaState, val: Option<LuaValue>) -> LuaType {
1342 let v = val.unwrap_or(LuaValue::Nil);
1343 state.push(v);
1344 let top = state.top_idx();
1345 state.get_at(top - 1).base_type()
1346}
1347
1348fn get_table_value(state: &LuaState, idx: i32) -> Option<GcRef<LuaTable>> {
1349 let t = index_to_value(state, idx);
1350 debug_assert!(matches!(t, LuaValue::Table(_)), "table expected");
1351 if let LuaValue::Table(tbl) = t {
1352 Some(tbl)
1353 } else {
1354 None
1355 }
1356}
1357
1358pub fn raw_get(state: &mut LuaState, idx: i32) -> LuaType {
1359 let t = get_table_value(state, idx);
1360 let top = state.top_idx();
1361 let key = state.get_at(top - 1);
1362 let val = t.as_ref().map(|tbl| tbl.get(&key));
1363 state.set_top_idx(top - 1);
1364 finish_raw_get(state, val)
1365}
1366
1367pub fn raw_get_i(state: &mut LuaState, idx: i32, n: i64) -> LuaType {
1368 let t = get_table_value(state, idx);
1369 let val = t.as_ref().map(|tbl| tbl.get_int(n));
1370 finish_raw_get(state, val)
1371}
1372
1373pub fn raw_get_p(state: &mut LuaState, idx: i32, p: *const core::ffi::c_void) -> LuaType {
1374 let t = get_table_value(state, idx);
1375 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1376 let val = t.as_ref().map(|tbl| tbl.get(&key));
1377 finish_raw_get(state, val)
1378}
1379
1380pub fn create_table(state: &mut LuaState, narray: i32, nrec: i32) -> Result<(), LuaError> {
1381 let t = state.new_table();
1382 if narray > 0 || nrec > 0 {
1383 t.resize(state, narray as usize, nrec as usize)?;
1384 }
1385 state.push(LuaValue::Table(t));
1386 state.gc_pre_collect_clear();
1387 state.gc().check_step();
1388 Ok(())
1389}
1390
1391pub fn get_metatable(state: &mut LuaState, objindex: i32) -> bool {
1392 let obj = index_to_value(state, objindex);
1393 let mt: Option<GcRef<LuaTable>> = match &obj {
1394 LuaValue::Table(t) => t.metatable(),
1395 LuaValue::UserData(u) => u.metatable(),
1396 other => {
1397 let idx = other.base_type() as usize;
1398 state.global().mt[idx].clone()
1399 }
1400 };
1401 if let Some(mt_table) = mt {
1402 state.push(LuaValue::Table(mt_table));
1403 true
1404 } else {
1405 false
1406 }
1407}
1408
1409pub fn get_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> LuaType {
1410 let o = index_to_value(state, idx);
1411 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1412 if let LuaValue::UserData(ref u) = o {
1413 let uv = u.uv.borrow();
1414 let uv_count = uv.len() as i32;
1415 if n <= 0 || n > uv_count {
1416 state.push(LuaValue::Nil);
1417 LuaType::None
1418 } else {
1419 let val = uv[(n - 1) as usize].clone();
1420 let t = val.base_type();
1421 state.push(val);
1422 t
1423 }
1424 } else {
1425 state.push(LuaValue::Nil);
1426 LuaType::None
1427 }
1428}
1429
1430fn aux_set_str(state: &mut LuaState, t: LuaValue, k: &[u8]) -> Result<(), LuaError> {
1433 let str_val = {
1434 let ts = state.intern_str(k)?;
1435 LuaValue::Str(ts)
1436 };
1437 let top = state.top_idx();
1442 let val = state.get_at(top - 1);
1443 state.table_set_with_tm(&t, str_val, val)?;
1444 state.pop();
1445 Ok(())
1446}
1447
1448pub fn set_global(state: &mut LuaState, name: &[u8]) -> Result<(), LuaError> {
1449 let g = get_global_table(state);
1450 aux_set_str(state, g, name)
1451}
1452
1453pub fn set_table(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1454 let t = index_to_value(state, idx);
1455 let top = state.top_idx();
1456 let key = state.get_at(top - 2);
1457 let val = state.get_at(top - 1);
1458 state.table_set_with_tm(&t, key, val)?;
1459 state.set_top_idx(top - 2);
1460 Ok(())
1461}
1462
1463pub fn set_field(state: &mut LuaState, idx: i32, k: &[u8]) -> Result<(), LuaError> {
1464 let t = index_to_value(state, idx);
1465 aux_set_str(state, t, k)
1466}
1467
1468pub fn set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1469 let t = index_to_value(state, idx);
1470 let top = state.top_idx();
1471 let val = state.get_at(top - 1);
1472 let key = LuaValue::Int(n);
1473 state.table_set_with_tm(&t, key, val)?;
1474 state.pop();
1475 Ok(())
1476}
1477
1478pub fn set_i_value(state: &mut LuaState, t: &LuaValue, n: i64) -> Result<(), LuaError> {
1484 let top = state.top_idx();
1485 let val = state.get_at(top - 1);
1486 let key = LuaValue::Int(n);
1487 state.table_set_with_tm(t, key, val)?;
1488 state.pop();
1489 Ok(())
1490}
1491
1492fn aux_raw_set(state: &mut LuaState, idx: i32, key: LuaValue, n: u32) -> Result<(), LuaError> {
1493 let t = get_table_value(state, idx)
1494 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1495 let top = state.top_idx();
1496 let val = state.get_at(top - 1);
1497 t.raw_set(state, key, val)?;
1498 t.invalidate_tm_cache();
1499 let top_val = state.get_at(top - 1);
1500 state.gc().table_barrier_back(&t, &top_val);
1501 state.set_top_idx(top - n as i32);
1502 Ok(())
1503}
1504
1505pub fn raw_set(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
1506 let top = state.top_idx();
1507 let key = state.get_at(top - 2);
1508 aux_raw_set(state, idx, key, 2)
1509}
1510
1511pub fn raw_set_p(
1512 state: &mut LuaState,
1513 idx: i32,
1514 p: *const core::ffi::c_void,
1515) -> Result<(), LuaError> {
1516 let key = LuaValue::LightUserData(p as *mut core::ffi::c_void);
1517 aux_raw_set(state, idx, key, 1)
1518}
1519
1520pub fn raw_set_i(state: &mut LuaState, idx: i32, n: i64) -> Result<(), LuaError> {
1521 let t = get_table_value(state, idx)
1522 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
1523 let top = state.top_idx();
1524 let val = state.get_at(top - 1);
1525 t.raw_set_int(state, n, val)?;
1526 let top_val = state.get_at(top - 1);
1527 state.gc().table_barrier_back(&t, &top_val);
1528 state.pop();
1529 Ok(())
1530}
1531
1532fn metatable_has_gc(state: &LuaState, mt: &GcRef<LuaTable>) -> bool {
1537 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1538 !matches!(mt.get_short_str(&name), LuaValue::Nil)
1539}
1540
1541fn register_finalizable_object(state: &mut LuaState, object: FinalizerObject) {
1542 let heap_ptr = object.heap_ptr();
1543 let mut g = state.global_mut();
1544 if g.finalizers.push_pending_unique(object) {
1545 if let Some(ptr) = heap_ptr {
1546 g.heap.move_allgc_to_finobj(ptr);
1547 }
1548 }
1549}
1550
1551struct Udata51RosterEntry {
1566 weak: lua_types::gc::GcWeak<LuaUserData>,
1567 submitted: std::cell::Cell<bool>,
1568}
1569
1570impl lua_gc::Udata51Probe for Udata51RosterEntry {
1571 fn is_alive(&self) -> bool {
1572 self.weak.upgrade().is_some()
1573 }
1574
1575 fn as_any(&self) -> &dyn std::any::Any {
1576 self
1577 }
1578}
1579
1580fn roster_v51_userdata(state: &LuaState, ud: &GcRef<LuaUserData>) {
1590 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1591 return;
1592 }
1593 let probe = std::rc::Rc::new(Udata51RosterEntry {
1594 weak: ud.downgrade(),
1595 submitted: std::cell::Cell::new(false),
1596 });
1597 state.global().heap.register_v51_udata(probe);
1598}
1599
1600pub fn scan_v51_finalizable_userdata(state: &mut LuaState) {
1612 if !matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
1613 return;
1614 }
1615 let probes = state.global().heap.scan_v51_finalizable();
1616 if probes.is_empty() {
1617 return;
1618 }
1619 let gc_name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1620 for probe in &probes {
1621 let entry = match probe.as_any().downcast_ref::<Udata51RosterEntry>() {
1622 Some(entry) => entry,
1623 None => continue,
1624 };
1625 if entry.submitted.get() {
1626 continue;
1627 }
1628 let ud = match entry.weak.upgrade() {
1629 Some(ud) => ud,
1630 None => continue,
1631 };
1632 let has_gc = match ud.metatable() {
1633 Some(mt) => !matches!(mt.get_short_str(&gc_name), LuaValue::Nil),
1634 None => false,
1635 };
1636 if has_gc {
1637 entry.submitted.set(true);
1638 register_finalizable_object(state, FinalizerObject::UserData(ud));
1639 }
1640 }
1641}
1642
1643pub fn run_pending_finalizers(state: &mut LuaState) {
1646 let _ = run_pending_finalizers_inner(state, false);
1647}
1648
1649const GC_FIN_MAX: usize = 10;
1650
1651pub fn run_pending_finalizers_inner(state: &mut LuaState, propagate: bool) -> Result<(), LuaError> {
1671 run_pending_finalizers_limited(state, propagate, None).map(|_| ())
1672}
1673
1674pub(crate) fn run_some_pending_finalizers_inner(
1675 state: &mut LuaState,
1676 propagate: bool,
1677) -> Result<usize, LuaError> {
1678 run_pending_finalizers_limited(state, propagate, Some(GC_FIN_MAX))
1679}
1680
1681fn run_pending_finalizers_limited(
1682 state: &mut LuaState,
1683 propagate: bool,
1684 limit: Option<usize>,
1685) -> Result<usize, LuaError> {
1686 let version = state.global().lua_version;
1687 let mut processed = 0usize;
1688 loop {
1689 if limit.map_or(false, |limit| processed >= limit) {
1690 break;
1691 }
1692 if !state.global().finalizers.has_to_be_finalized() {
1696 break;
1697 }
1698 let object = {
1708 let mut g = state.global_mut();
1709 let object = g
1710 .finalizers
1711 .pop_to_be_finalized()
1712 .expect("to-be-finalized checked non-empty");
1713 if let Some(ptr) = object.heap_ptr() {
1714 g.heap.move_tobefnz_to_allgc(ptr);
1715 }
1716 object
1717 };
1718 processed += 1;
1719 let mt = object.metatable();
1720 let gc_fn = match mt {
1721 Some(ref m) => {
1722 let name = state.global().tmname[crate::tagmethods::TagMethod::Gc as usize].clone();
1723 m.get_short_str(&name)
1724 }
1725 None => LuaValue::Nil,
1726 };
1727 if !matches!(gc_fn, LuaValue::Function(_)) {
1728 continue;
1729 }
1730 let saved_top = state.top_idx();
1731 let ci_top = state.current_call_info().top;
1732 if saved_top.0 < ci_top.0 {
1733 state.clear_stack_range(saved_top, ci_top);
1734 state.set_top(ci_top);
1735 }
1736 state.push(gc_fn);
1737 state.push(object.as_lua_value());
1738 let func_idx = state.top_idx() - 2;
1739 let _heap_guard = {
1740 let g = state.global.borrow();
1741 lua_gc::HeapGuard::push(&g.heap)
1742 };
1743 let old_allowhook = state.allowhook;
1744 let old_gcstp = state.global_mut().stop_gc_internal();
1745 state.allowhook = false;
1746 let caller_ci = state.ci;
1747 let caller_status = state.get_ci(caller_ci).callstatus;
1748 state.get_ci_mut(caller_ci).callstatus = caller_status | crate::state::CIST_FIN;
1749 let status = crate::do_::pcall(state, |s| s.call_no_yield(func_idx, 0), func_idx, 0);
1750 let finalizer_error: Option<LuaValue> = if status != LuaStatus::Ok {
1754 Some(state.get_at(state.top_idx() - 1).clone())
1755 } else {
1756 None
1757 };
1758 state.get_ci_mut(caller_ci).callstatus = caller_status;
1759 state.allowhook = old_allowhook;
1760 state.global_mut().set_gc_stop_flags(old_gcstp);
1761 state.set_top(saved_top);
1762
1763 if let Some(errobj) = finalizer_error {
1764 match version {
1765 lua_types::LuaVersion::V51 if propagate => {
1766 return Err(LuaError::from_value(errobj));
1773 }
1774 lua_types::LuaVersion::V52 | lua_types::LuaVersion::V53 if propagate => {
1775 let msg: Vec<u8> = match &errobj {
1782 LuaValue::Str(s) => s.as_bytes().to_vec(),
1783 _ => b"no message".to_vec(),
1784 };
1785 let mut wrapped = b"error in __gc metamethod (".to_vec();
1786 wrapped.extend_from_slice(&msg);
1787 wrapped.push(b')');
1788 let interned = state.intern_str(&wrapped)?;
1789 return Err(LuaError::from_value(LuaValue::Str(interned)));
1790 }
1791 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55 if propagate => {
1792 let msg: Vec<u8> = match &errobj {
1796 LuaValue::Str(s) => s.as_bytes().to_vec(),
1797 _ => b"error object is not a string".to_vec(),
1798 };
1799 state.emit_warning(b"error in ", true);
1800 state.emit_warning(b"__gc", true);
1801 state.emit_warning(b" (", true);
1802 state.emit_warning(&msg, true);
1803 state.emit_warning(b")", false);
1804 }
1805 _ => {}
1806 }
1807 }
1808 }
1809 Ok(processed)
1813}
1814
1815const CLOSE_FINALIZER_MAX_PASSES: usize = 1000;
1823
1824pub fn run_close_finalizers(state: &mut LuaState) {
1851 let mut seen = std::collections::HashSet::<usize>::new();
1852 {
1853 let g = state.global();
1854 for object in g.finalizers.to_be_finalized() {
1855 seen.insert(object.identity());
1856 }
1857 }
1858 let mut passes = 0usize;
1859 loop {
1860 {
1861 let g = state.global();
1862 if g.finalizers.pending_len() == 0 && !g.finalizers.has_to_be_finalized() {
1863 break;
1864 }
1865 }
1866 passes += 1;
1867 if passes > CLOSE_FINALIZER_MAX_PASSES {
1868 break;
1869 }
1870 let pending: Vec<FinalizerObject> = state.global_mut().finalizers.take_pending();
1871 {
1872 let mut g = state.global_mut();
1873 for object in pending.into_iter().rev() {
1874 let heap_ptr = object.heap_ptr();
1875 if seen.insert(object.identity()) {
1876 g.finalizers.push_to_be_finalized(object);
1877 if let Some(ptr) = heap_ptr {
1878 g.heap.move_finobj_to_tobefnz(ptr);
1879 }
1880 }
1881 }
1882 }
1883 run_pending_finalizers(state);
1884 }
1885}
1886
1887fn collect_live_weak_tables(state: &mut LuaState) -> Vec<GcRef<lua_types::value::LuaTable>> {
1893 let mut g = state.global_mut();
1894 g.weak_tables_registry.live_snapshot()
1895}
1896
1897pub fn set_metatable(state: &mut LuaState, objindex: i32) -> Result<bool, LuaError> {
1898 let top = state.top_idx();
1899 let mt_val = state.get_at(top - 1);
1900 let mt: Option<GcRef<LuaTable>> = if matches!(mt_val, LuaValue::Nil) {
1901 None
1902 } else {
1903 debug_assert!(matches!(mt_val, LuaValue::Table(_)), "table expected");
1904 if let LuaValue::Table(t) = mt_val {
1905 Some(t)
1906 } else {
1907 None
1908 }
1909 };
1910
1911 let obj = index_to_value(state, objindex);
1912 match obj {
1913 LuaValue::Table(ref tbl) => {
1914 if mt.is_some() {
1915 state.gc().obj_barrier(tbl, mt.as_ref().unwrap());
1916 }
1917 tbl.set_metatable(mt.clone());
1918 if tbl.weak_mode() == 0 {
1919 state
1920 .global_mut()
1921 .weak_tables_registry
1922 .remove_identity(tbl.identity());
1923 } else {
1924 state
1925 .global_mut()
1926 .weak_tables_registry
1927 .push_unique(WeakTableEntry::new(tbl));
1928 }
1929 let tables_finalizable =
1939 !matches!(state.global().lua_version, lua_types::LuaVersion::V51);
1940 if tables_finalizable {
1941 if let Some(ref mt_table) = mt {
1942 if metatable_has_gc(state, mt_table) {
1943 register_finalizable_object(state, FinalizerObject::Table(tbl.clone()));
1944 }
1945 }
1946 }
1947 }
1948 LuaValue::UserData(ref ud) => {
1949 if let Some(ref mt_table) = mt {
1950 state.gc().obj_barrier(ud, mt_table);
1951 if metatable_has_gc(state, mt_table) {
1952 register_finalizable_object(state, FinalizerObject::UserData(ud.clone()));
1953 }
1954 roster_v51_userdata(state, ud);
1959 }
1960 ud.set_metatable(mt);
1961 }
1962 ref other => {
1963 let idx = other.base_type() as usize;
1964 state.global_mut().mt[idx] = mt;
1965 }
1966 }
1967 state.pop();
1968 Ok(true)
1969}
1970
1971pub fn set_i_uservalue(state: &mut LuaState, idx: i32, n: i32) -> Result<bool, LuaError> {
1972 let o = index_to_value(state, idx);
1973 debug_assert!(matches!(o, LuaValue::UserData(_)), "full userdata expected");
1974 let top = state.top_idx();
1975 let val = state.get_at(top - 1);
1976 let res = if let LuaValue::UserData(ref ud) = o {
1977 let mut uv = ud.uv.borrow_mut();
1978 let nuvalue = uv.len() as i32;
1979 if n < 1 || n > nuvalue {
1980 false
1981 } else {
1982 uv[(n - 1) as usize] = val.clone();
1983 drop(uv);
1984 state.gc().barrier_back(ud, &val);
1985 true
1986 }
1987 } else {
1988 false
1989 };
1990 state.pop();
1991 Ok(res)
1992}
1993
1994pub fn call_k(
1998 state: &mut LuaState,
1999 nargs: i32,
2000 nresults: i32,
2001 ctx: isize,
2002 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
2003) -> Result<(), LuaError> {
2004 let top = state.top_idx();
2005 let func_idx = top - (nargs + 1);
2006 if k.is_some() && state.is_yieldable() {
2012 let ci_idx = state.ci;
2013 {
2014 let ci = state.get_ci_mut(ci_idx);
2015 ci.set_u_c_k(k);
2016 ci.set_u_c_ctx(ctx);
2017 }
2018 state.call_at(func_idx, nresults)?;
2019 } else {
2020 state.call_no_yield(func_idx, nresults)?;
2021 }
2022 state.adjust_results(nresults);
2023 Ok(())
2024}
2025
2026pub fn pcall_k(
2027 state: &mut LuaState,
2028 nargs: i32,
2029 nresults: i32,
2030 errfunc: i32,
2031 ctx: isize,
2032 k: Option<fn(&mut LuaState, i32, isize) -> Result<usize, LuaError>>,
2033) -> Result<LuaStatus, LuaError> {
2034 let _heap_guard = {
2039 let g = state.global.borrow();
2040 lua_gc::HeapGuard::push(&g.heap)
2045 };
2046 let err_handler_idx: isize = if errfunc == 0 {
2047 0
2048 } else {
2049 let o = index_to_stack_idx(state, errfunc);
2050 debug_assert!(
2051 matches!(state.get_at(o), LuaValue::Function(_)),
2052 "error handler must be a function"
2053 );
2054 o.0 as isize
2055 };
2056 let top = state.top_idx();
2057 let func_idx = top - (nargs + 1);
2058 if k.is_none() || !state.is_yieldable() {
2059 state.protected_call_raw(func_idx, nresults, StackIdx(err_handler_idx as u32))?;
2060 state.adjust_results(nresults);
2061 return Ok(LuaStatus::Ok);
2062 }
2063 let ci_idx = state.ci;
2069 let allow = state.allowhook;
2070 let saved_errfunc = state.errfunc;
2071 {
2072 let ci = state.get_ci_mut(ci_idx);
2073 ci.set_u_c_k(k);
2074 ci.set_u_c_ctx(ctx);
2075 ci.set_u2_funcidx(func_idx.0 as i32);
2076 ci.set_u_c_old_errfunc(saved_errfunc);
2077 ci.set_oah(allow);
2078 ci.callstatus |= crate::state::CIST_YPCALL;
2079 }
2080 state.errfunc = err_handler_idx;
2081 let call_result = crate::do_::call(state, func_idx, nresults);
2082 match call_result {
2083 Ok(()) => {
2084 state.get_ci_mut(ci_idx).callstatus &= !crate::state::CIST_YPCALL;
2087 state.errfunc = saved_errfunc;
2088 state.adjust_results(nresults);
2089 Ok(LuaStatus::Ok)
2090 }
2091 Err(crate::state::LuaError::Yield) => {
2092 Err(crate::state::LuaError::Yield)
2096 }
2097 Err(e) => {
2098 Err(e)
2102 }
2103 }
2104}
2105
2106fn first_upvalue_is_env(lcl: &lua_types::closure::LuaLClosure) -> bool {
2120 lcl.proto
2121 .upvalues
2122 .first()
2123 .and_then(|uv| uv.name.as_ref())
2124 .is_some_and(|s| s.as_bytes() == b"_ENV")
2125}
2126
2127pub fn load(
2128 state: &mut LuaState,
2129 reader: crate::zio::ChunkReader,
2130 chunkname: Option<&[u8]>,
2131 mode: Option<&[u8]>,
2132) -> Result<LuaStatus, LuaError> {
2133 let _heap_guard = {
2141 let g = state.global.borrow();
2142 lua_gc::HeapGuard::push(&g.heap)
2143 };
2144 let name = chunkname.unwrap_or(b"?");
2145 let z = crate::zio::ZIO::new(reader);
2146 let status = state.protected_parser(z, name, mode);
2147 if status == LuaStatus::Ok {
2148 let top = state.top_idx();
2149 let func_val = state.get_at(top - 1);
2150 if let LuaValue::Function(LuaClosure::Lua(lcl)) = func_val {
2151 let inject_env = match state.global().lua_version {
2152 lua_types::LuaVersion::V51 => first_upvalue_is_env(&lcl),
2153 lua_types::LuaVersion::V52 => lcl.upvals.len() == 1,
2154 _ => !lcl.upvals.is_empty(),
2155 };
2156 if inject_env {
2157 let gt = get_global_table(state);
2158 let uv = state.new_upval_closed(gt);
2159 lcl.set_upval(0, uv);
2160 state.gc().obj_barrier(&lcl, &uv);
2161 }
2162 }
2163 }
2164 Ok(status)
2165}
2166
2167pub fn dump(
2168 state: &LuaState,
2169 writer: &mut dyn FnMut(&[u8]) -> Result<(), LuaError>,
2170 strip: bool,
2171) -> Result<bool, LuaError> {
2172 let top = state.top_idx();
2173 let o = state.get_at(top - 1);
2174 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = o {
2175 crate::dump::dump(state, &lcl.proto, writer, strip)?;
2176 Ok(true)
2177 } else {
2178 Ok(false)
2179 }
2180}
2181
2182pub fn status(state: &LuaState) -> LuaStatus {
2183 LuaStatus::from_raw(state.status as i32)
2184}
2185
2186#[repr(i32)]
2190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2191pub enum GcWhat {
2192 Stop = 0,
2193 Restart = 1,
2194 Collect = 2,
2195 Count = 3,
2196 CountB = 4,
2197 Step = 5,
2198 SetPause = 6,
2199 SetStepMul = 7,
2200 IsRunning = 9,
2201 Gen = 10,
2202 Inc = 11,
2203}
2204
2205pub enum GcArgs {
2207 Stop,
2208 Restart,
2209 Collect,
2210 Count,
2211 CountB,
2212 Step {
2213 data: i32,
2214 },
2215 SetPause {
2216 value: i32,
2217 },
2218 SetStepMul {
2219 value: i32,
2220 },
2221 IsRunning,
2222 Gen {
2223 minormul: i32,
2224 majormul: i32,
2225 },
2226 Inc {
2227 pause: i32,
2228 stepmul: i32,
2229 stepsize: i32,
2230 },
2231 Param {
2234 param: usize,
2235 value: i64,
2236 },
2237}
2238
2239pub fn gc(state: &mut LuaState, args: GcArgs) -> i32 {
2240 if let GcArgs::Param { param, value } = &args {
2244 return state.global_mut().gc55_param(*param, *value) as i32;
2245 }
2246 if state.global().is_gc_stopped_internally() {
2247 return -1;
2248 }
2249 match args {
2250 GcArgs::Stop => {
2251 state.global_mut().set_gc_stop_user();
2252 }
2253 GcArgs::Restart => {
2254 {
2255 let mut g = state.global_mut();
2256 crate::state::set_debt(&mut *g, 0);
2257 }
2258 state.global_mut().clear_gc_stop();
2259 }
2260 GcArgs::Collect => {
2261 if !state.allowhook {
2262 return 0;
2263 }
2264 scan_v51_finalizable_userdata(state);
2268 state.gc().full_collect();
2274 if let Err(e) = run_pending_finalizers_inner(state, true) {
2282 state.global_mut().gc_finalizer_error = Some(e.into_value());
2283 }
2284 }
2285 GcArgs::Count => {
2286 let g = state.global();
2287 let total = g.heap.bytes_used();
2288 return (total >> 10) as i32;
2289 }
2290 GcArgs::CountB => {
2291 let g = state.global();
2292 let total = g.heap.bytes_used();
2293 return (total & 0x3ff) as i32;
2294 }
2295 GcArgs::Step { data } => {
2296 let old_stp = {
2297 let mut g = state.global_mut();
2298 let old = g.gc_stop_flags();
2299 g.clear_gc_stop();
2300 old
2301 };
2302 let stepmul = (state.global().gc_stepmul_param() as isize | 1).max(1);
2309 let work_units = if data == 0 {
2310 stepmul
2311 } else {
2312 let raw = (data as isize).saturating_mul(stepmul);
2313 raw.max(1)
2314 };
2315 let debt_for_result = if data == 0 {
2316 let mut g = state.global_mut();
2317 crate::state::set_debt(&mut *g, 0);
2318 0
2319 } else {
2320 let debt = data as isize * 1024 + state.global().gc_debt();
2321 let mut g = state.global_mut();
2322 crate::state::set_debt(&mut *g, debt);
2323 debt
2324 };
2325 let gen_mode = state.global().is_gen_mode();
2326 let cycle_complete = if gen_mode {
2327 if data == 0 {
2328 state.gc().generational_step_minor_only();
2329 } else {
2330 state.gc().generational_step();
2331 }
2332 if state.global().finalizers.has_to_be_finalized() {
2333 if let Err(e) = run_pending_finalizers_inner(state, true) {
2334 state.global_mut().gc_finalizer_error = Some(e.into_value());
2335 }
2336 }
2337 debt_for_result > 0 && state.global().gc_at_pause()
2338 } else if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2339 && state.global().finalizers.has_to_be_finalized()
2340 {
2341 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2342 state.global_mut().gc_finalizer_error = Some(e.into_value());
2343 }
2344 if state.global().finalizers.has_to_be_finalized() {
2345 false
2346 } else {
2347 state.global().heap.finish_callfin_phase()
2348 }
2349 } else {
2350 let completed = state.gc().incremental_step(work_units);
2351 if state.global().heap.gc_state() == lua_gc::GcState::CallFin
2352 && state.global().finalizers.has_to_be_finalized()
2353 {
2354 if let Err(e) = run_some_pending_finalizers_inner(state, true) {
2355 state.global_mut().gc_finalizer_error = Some(e.into_value());
2356 }
2357 if state.global().finalizers.has_to_be_finalized() {
2358 false
2359 } else {
2360 state.global().heap.finish_callfin_phase()
2361 }
2362 } else {
2363 completed
2364 }
2365 };
2366 state.global_mut().set_gc_stop_flags(old_stp);
2367 {
2369 let heap_state = state.global().heap.gc_state();
2370 let mut g = state.global_mut();
2371 g.gcstate = if heap_state.is_pause() { 0 } else { 1 };
2372 }
2373 return if cycle_complete { 1 } else { 0 };
2374 }
2375 GcArgs::SetPause { value } => {
2376 let old = state.global().gc_pause_param();
2377 state.global_mut().set_gc_pause_param(value);
2378 return old;
2379 }
2380 GcArgs::SetStepMul { value } => {
2381 let old = state.global().gc_stepmul_param();
2382 state.global_mut().set_gc_stepmul_param(value);
2383 return old;
2384 }
2385 GcArgs::IsRunning => {
2386 return state.global().gc_running() as i32;
2387 }
2388 GcArgs::Gen { minormul, majormul } => {
2389 let old_mode = if state.global().is_gen_mode() {
2390 10i32
2391 } else {
2392 11i32
2393 };
2394 if minormul != 0 {
2395 state.global_mut().genminormul = minormul as u8;
2396 }
2397 if majormul != 0 {
2398 state.global_mut().set_gc_genmajormul(majormul);
2399 }
2400 state.gc().change_mode(crate::state::GcKind::Generational);
2401 return old_mode;
2402 }
2403 GcArgs::Inc {
2404 pause,
2405 stepmul,
2406 stepsize,
2407 } => {
2408 let old_mode = if state.global().is_gen_mode() {
2409 10i32
2410 } else {
2411 11i32
2412 };
2413 if pause != 0 {
2414 state.global_mut().set_gc_pause_param(pause);
2415 }
2416 if stepmul != 0 {
2417 state.global_mut().set_gc_stepmul_param(stepmul);
2418 }
2419 if stepsize != 0 {
2420 state.global_mut().gcstepsize = stepsize as u8;
2421 }
2422 state.gc().change_mode(crate::state::GcKind::Incremental);
2423 return old_mode;
2424 }
2425 GcArgs::Param { .. } => unreachable!("Param handled before the finalizer guard"),
2426 }
2427 0
2428}
2429
2430pub fn configure_startup_gc_mode(state: &mut LuaState) {
2437 if matches!(
2438 state.global().lua_version,
2439 lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
2440 ) {
2441 let _ = gc(state, GcArgs::Restart);
2442 let _ = gc(
2443 state,
2444 GcArgs::Gen {
2445 minormul: 0,
2446 majormul: 0,
2447 },
2448 );
2449 }
2450}
2451
2452pub fn lua_error(state: &mut LuaState) -> Result<Infallible, LuaError> {
2458 let top = state.top_idx();
2459 let errobj = state.get_at(top - 1);
2460 let is_mem_err = if let LuaValue::Str(ref s) = errobj {
2461 let memerr = state.global().memerrmsg.clone();
2462 GcRef::ptr_eq(s, &memerr)
2463 } else {
2464 false
2465 };
2466 if is_mem_err {
2467 Err(LuaError::Memory)
2468 } else {
2469 Err(LuaError::from_value(errobj))
2470 }
2471}
2472
2473fn normalize_float_only_next_key(state: &LuaState, key: LuaValue) -> LuaValue {
2483 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2484 return key;
2485 }
2486 if let LuaValue::Float(f) = key {
2487 let k = f as i64;
2488 if k as f64 == f {
2489 return LuaValue::Int(k);
2490 }
2491 }
2492 key
2493}
2494
2495pub fn next(state: &mut LuaState, idx: i32) -> Result<bool, LuaError> {
2496 let t = get_table_value(state, idx)
2497 .ok_or_else(|| LuaError::runtime(format_args!("table expected")))?;
2498 let top = state.top_idx();
2499 let key = normalize_float_only_next_key(state, state.get_at(top - 1));
2500 match t.next(key)? {
2501 Some((next_key, next_val)) => {
2502 state.set_at(top - 1, next_key);
2503 state.push(next_val);
2504 Ok(true)
2505 }
2506 None => {
2507 state.set_top_idx(top - 1);
2508 Ok(false)
2509 }
2510 }
2511}
2512
2513pub fn to_close(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2514 let _level = index_to_stack_idx(state, idx);
2515 Ok(())
2516}
2517
2518pub fn concat(state: &mut LuaState, n: i32) -> Result<(), LuaError> {
2519 if n > 0 {
2520 state.concat(n)?;
2521 } else {
2522 let empty = state.intern_str(b"")?;
2523 state.push(LuaValue::Str(empty));
2524 }
2525 state.gc_pre_collect_clear();
2526 state.gc().check_step();
2527 Ok(())
2528}
2529
2530pub fn len(state: &mut LuaState, idx: i32) -> Result<(), LuaError> {
2531 let t = index_to_value(state, idx);
2532 let result = state.obj_len(&t)?;
2533 state.push(result);
2534 Ok(())
2535}
2536
2537pub fn set_warn_f(state: &mut LuaState, f: Option<Box<dyn FnMut(&[u8], bool)>>) {
2541 state.global_mut().warnf = f;
2543}
2544
2545pub fn warning(state: &mut LuaState, msg: &[u8], tocont: bool) {
2546 state.emit_warning(msg, tocont);
2547}
2548
2549pub fn new_userdata_uv(
2550 state: &mut LuaState,
2551 size: usize,
2552 nuvalue: i32,
2553) -> Result<GcRef<LuaUserData>, LuaError> {
2554 debug_assert!(nuvalue >= 0 && nuvalue < u16::MAX as i32, "invalid value");
2555 let u = state.new_userdata(size, nuvalue as usize)?;
2556 state.push(LuaValue::UserData(u.clone()));
2557 state.gc_pre_collect_clear();
2558 state.gc().check_step();
2559 Ok(u)
2560}
2561
2562fn aux_upvalue(state: &LuaState, fi: &LuaValue, n: i32) -> Option<(Vec<u8>, LuaValue)> {
2568 match fi {
2569 LuaValue::Function(LuaClosure::C(ccl)) => {
2570 let upvalues = ccl.upvalues.borrow();
2571 let nupvalues = upvalues.len() as i32;
2572 if n < 1 || n > nupvalues {
2573 return None;
2574 }
2575 Some((Vec::new(), upvalues[(n - 1) as usize].clone()))
2576 }
2577 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2578 let nupvalues = lcl.upvals.len() as i32;
2579 if n < 1 || n > nupvalues {
2580 return None;
2581 }
2582 let val = state.upvalue_get(lcl, (n - 1) as usize);
2583 let no_name: &[u8] = if state.global().lua_version == lua_types::LuaVersion::V53 {
2587 b"(*no name)"
2588 } else {
2589 b"(no name)"
2590 };
2591 let name: Vec<u8> = lcl
2592 .proto
2593 .upvalues
2594 .get((n - 1) as usize)
2595 .and_then(|ud| ud.name.as_ref())
2596 .map(|s| s.as_bytes().to_vec())
2597 .unwrap_or_else(|| no_name.to_vec());
2598 Some((name, val))
2599 }
2600 _ => None,
2601 }
2602}
2603
2604pub fn get_upvalue(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2605 let fi = index_to_value(state, funcindex);
2606 if let Some((name, val)) = aux_upvalue(state, &fi, n) {
2607 state.push(val);
2608 Some(name)
2609 } else {
2610 None
2611 }
2612}
2613
2614pub fn setup_value(state: &mut LuaState, funcindex: i32, n: i32) -> Option<Vec<u8>> {
2615 let fi = index_to_value(state, funcindex);
2616 let (name, _) = aux_upvalue(state, &fi, n)?;
2617 let new_val = state.pop();
2618 match &fi {
2619 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2620 state.upvalue_set(lcl, (n - 1) as usize, new_val).ok()?;
2621 }
2622 LuaValue::Function(LuaClosure::C(ccl)) => {
2623 let idx = (n - 1) as usize;
2624 {
2625 let mut upvalues = ccl.upvalues.borrow_mut();
2626 if idx >= upvalues.len() {
2627 return None;
2628 }
2629 upvalues[idx] = new_val.clone();
2630 }
2631 state.gc().barrier(ccl, &new_val);
2632 }
2633 _ => return None,
2634 }
2635 Some(name)
2636}
2637
2638fn get_upval_ref_idx(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2641 let fi = index_to_value(state, fidx);
2642 debug_assert!(
2643 matches!(fi, LuaValue::Function(LuaClosure::Lua(_))),
2644 "Lua function expected"
2645 );
2646 if let LuaValue::Function(LuaClosure::Lua(ref lcl)) = fi {
2647 let sizeupvalues = lcl.upvals.len() as i32;
2648 if n >= 1 && n <= sizeupvalues {
2649 Some((n - 1) as usize)
2650 } else {
2651 None
2652 }
2653 } else {
2654 None
2655 }
2656}
2657
2658pub fn upvalue_id(state: &LuaState, fidx: i32, n: i32) -> Option<usize> {
2660 let fi = index_to_value(state, fidx);
2661 match &fi {
2662 LuaValue::Function(LuaClosure::Lua(lcl)) => {
2663 let idx = get_upval_ref_idx(state, fidx, n)?;
2664 Some(GcRef::identity(&lcl.upval(idx)))
2666 }
2667 LuaValue::Function(LuaClosure::C(ccl)) => {
2668 let upvalues = ccl.upvalues.borrow();
2669 if n >= 1 && n <= upvalues.len() as i32 {
2670 Some(GcRef::identity(ccl) ^ (n as usize))
2674 } else {
2675 None
2676 }
2677 }
2678 LuaValue::Function(LuaClosure::LightC(_)) => None,
2679 _ => {
2680 debug_assert!(false, "function expected");
2681 None
2682 }
2683 }
2684}
2685
2686pub fn upvalue_join(state: &mut LuaState, fidx1: i32, n1: i32, fidx2: i32, n2: i32) {
2688 let idx1 = match get_upval_ref_idx(state, fidx1, n1) {
2689 Some(i) => i,
2690 None => return,
2691 };
2692 let idx2 = match get_upval_ref_idx(state, fidx2, n2) {
2693 Some(i) => i,
2694 None => return,
2695 };
2696 let f1 = index_to_value(state, fidx1);
2697 let f2 = index_to_value(state, fidx2);
2698 if let (LuaValue::Function(LuaClosure::Lua(lcl1)), LuaValue::Function(LuaClosure::Lua(lcl2))) =
2699 (&f1, &f2)
2700 {
2701 let shared = lcl2.upval(idx2);
2702 lcl1.set_upval(idx1, shared);
2703 state.gc().obj_barrier(lcl1, &shared);
2704 }
2705}